Mercurial > projects > ldc
diff tango/tango/io/selector/AbstractSelector.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tango/tango/io/selector/AbstractSelector.d Fri Jan 11 17:57:40 2008 +0100 @@ -0,0 +1,394 @@ +/******************************************************************************* + copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved + license: BSD style: $(LICENSE) + author: Juan Jose Comellas <juanjo@comellas.com.ar> +*******************************************************************************/ + +module tango.io.selector.AbstractSelector; + +public import tango.io.model.IConduit; +public import tango.io.selector.SelectorException; + +private import tango.io.selector.model.ISelector; +private import tango.sys.Common; +private import tango.stdc.errno; + +version (Windows) +{ + public struct timeval + { + int tv_sec; // seconds + int tv_usec; // microseconds + } +} + +/** + * Base class for all selectors. + * + * A selector is a multiplexor for I/O events associated to a Conduit. + * All selectors must implement this interface. + * + * A selector needs to be initialized by calling the open() method to pass + * it the initial amount of conduits that it will handle and the maximum + * amount of events that will be returned per call to select(). In both cases, + * these values are only hints and may not even be used by the specific + * ISelector implementation you choose to use, so you cannot make any + * assumptions regarding what results from the call to select() (i.e. you + * may receive more or less events per call to select() than what was passed + * in the 'maxEvents' argument. The amount of conduits that the selector can + * manage will be incremented dynamically if necessary. + * + * To add, modify or remove conduit registrations to the selector you use + * the register(), reregister() and unregister() methods respectively. + * + * To wait for events from the conduits you need to call any of the select() + * methods. The selector cannot be modified from another thread while + * blocking on a call to these methods. + * + * Once the selector is no longer used you must call the close() method so + * that the selector can free any resources it may have allocated in the call + * to open(). + * + * See_Also: ISelector + * + * Examples: + * --- + * import tango.io.selector.model.ISelector; + * import tango.io.Stdout; + * import tango.net.SocketConduit; + * + * AbstractSelector selector; + * SocketConduit conduit1; + * SocketConduit conduit2; + * MyClass object1; + * MyClass object2; + * uint eventCount; + * + * // Initialize the selector assuming that it will deal with 2 conduits and + * // will receive 2 events per invocation to the select() method. + * selector.open(2, 2); + * + * selector.register(conduit, Event.Read, object1); + * selector.register(conduit, Event.Write, object2); + * + * eventCount = selector.select(); + * + * if (eventCount > 0) + * { + * char[16] buffer; + * int count; + * + * foreach (SelectionKey key, selector.selectedSet()) + * { + * if (key.isReadable()) + * { + * count = (cast(SocketConduit) key.conduit).read(buffer); + * if (count != IConduit.Eof) + * { + * Stdout.format("Received '{0}' from peer\n", buffer[0..count]); + * selector.reregister(key.conduit, Event.Write, key.attachment); + * } + * else + * { + * selector.unregister(key.conduit); + * key.conduit.close(); + * } + * } + * + * if (key.isWritable()) + * { + * count = (cast(SocketConduit) key.conduit).write("MESSAGE"); + * if (count != IConduit.Eof) + * { + * Stdout("Sent 'MESSAGE' to peer\n"); + * selector.reregister(key.conduit, Event.Read, key.attachment); + * } + * else + * { + * selector.unregister(key.conduit); + * key.conduit.close(); + * } + * } + * + * if (key.isError() || key.isHangup() || key.isInvalidHandle()) + * { + * selector.unregister(key.conduit); + * key.conduit.close(); + * } + * } + * } + * + * selector.close(); + * --- + */ +abstract class AbstractSelector: ISelector +{ + /** + * Restart interrupted system calls when blocking inside a call to select. + */ + protected bool _restartInterruptedSystemCall = true; + + /** + * Indicates whether interrupted system calls will be restarted when + * blocking inside a call to select. + */ + public bool restartInterruptedSystemCall() + { + return _restartInterruptedSystemCall; + } + + /** + * Sets whether interrupted system calls will be restarted when + * blocking inside a call to select. + */ + public void restartInterruptedSystemCall(bool value) + { + _restartInterruptedSystemCall = value; + } + + /** + * Initialize the selector. + * + * Params: + * size = value that provides a hint for the maximum amount of + * conduits that will be registered + * maxEvents = value that provides a hint for the maximum amount of + * conduit events that will be returned in the selection + * set per call to select. + */ + public abstract void open(uint size, uint maxEvents); + + /** + * Free any operating system resources that may have been allocated in the + * call to open(). + * + * Remarks: + * Not all of the selectors need to free resources other than allocated + * memory, but those that do will normally also add a call to close() in + * their destructors. + */ + public abstract void close(); + + /** + * Associate a conduit to the selector and track specific I/O events. + * + * Params: + * conduit = conduit that will be associated to the selector + * events = bit mask of Event values that represent the events that + * will be tracked for the conduit. + * attachment = optional object with application-specific data that will + * be available when an event is triggered for the conduit + * + * Examples: + * --- + * AbstractSelector selector; + * SocketConduit conduit; + * MyClass object; + * + * selector.register(conduit, Event.Read | Event.Write, object); + * --- + */ + public abstract void register(ISelectable conduit, Event events, + Object attachment); + + /** + * Modify the events that are being tracked or the 'attachment' field + * for an already registered conduit. + * + * Params: + * conduit = conduit that will be associated to the selector + * events = bit mask of Event values that represent the events that + * will be tracked for the conduit. + * attachment = optional object with application-specific data that will + * be available when an event is triggered for the conduit + * + * Remarks: + * The 'attachment' member of the SelectionKey will always be overwritten, + * even if it's null. + * + * Examples: + * --- + * AbstractSelector selector; + * SocketConduit conduit; + * MyClass object; + * + * selector.reregister(conduit, Event.Write, object); + * --- + */ + public abstract void reregister(ISelectable conduit, Event events, + Object attachment); + + /** + * Remove a conduit from the selector. + * + * Params: + * conduit = conduit that had been previously associated to the + * selector; it can be null. + * + * Remarks: + * Unregistering a null conduit is allowed and no exception is thrown + * if this happens. + */ + public abstract void unregister(ISelectable conduit); + + /** + * Wait for I/O events from the registered conduits for a specified + * amount of time. + * + * Returns: + * The amount of conduits that have received events; 0 if no conduits + * have received events within the specified timeout; and -1 if the + * wakeup() method has been called from another thread. + * + * Remarks: + * This method is the same as calling select(TimeSpan.max). + */ + public int select() + { + return select(TimeSpan.max); + } + + /** + * Wait for I/O events from the registered conduits for a specified + * amount of time. + * + * Note: This representation of timeout is not always accurate, so it is + * possible that the function will return with a timeout before the + * specified period. For more accuracy, use the TimeSpan version. + * + * Params: + * timeout = the maximum amount of time in seconds that the + * selector will wait for events from the conduits; the + * amount of time is relative to the current system time + * (i.e. just the number of milliseconds that the selector + * has to wait for the events). + * + * Returns: + * The amount of conduits that have received events; 0 if no conduits + * have received events within the specified timeout. + */ + public int select(double timeout) + { + return select(TimeSpan.interval(timeout)); + } + + /** + * Wait for I/O events from the registered conduits for a specified + * amount of time. + * + * Params: + * timeout = TimeSpan with the maximum amount of time that the + * selector will wait for events from the conduits; the + * amount of time is relative to the current system time + * (i.e. just the number of milliseconds that the selector + * has to wait for the events). + * + * Returns: + * The amount of conduits that have received events; 0 if no conduits + * have received events within the specified timeout; and -1 if the + * wakeup() method has been called from another thread. + */ + public abstract int select(TimeSpan timeout); + + /** + * Causes the first call to select() that has not yet returned to return + * immediately. + * + * If another thread is currently blocked in an call to any of the + * select() methods then that call will return immediately. If no + * selection operation is currently in progress then the next invocation + * of one of these methods will return immediately. In any case the value + * returned by that invocation may be non-zero. Subsequent invocations of + * the select() methods will block as usual unless this method is invoked + * again in the meantime. + */ + // public abstract void wakeup(); + + /** + * Return the selection set resulting from the call to any of the select() + * methods. + * + * Remarks: + * If the call to select() was unsuccessful or it did not return any + * events, the returned value will be null. + */ + public abstract ISelectionSet selectedSet(); + + /** + * Return the selection key resulting from the registration of a conduit + * to the selector. + * + * Remarks: + * If the conduit is not registered to the selector the returned + * value will be null. No exception will be thrown by this method. + */ + public abstract SelectionKey key(ISelectable conduit); + + /** + * Cast the time duration to a C timeval struct. + */ + public timeval* toTimeval(timeval* tv, TimeSpan interval) + in + { + assert(tv !is null); + } + body + { + tv.tv_sec = cast(typeof(tv.tv_sec)) interval.seconds; + tv.tv_usec = cast(typeof(tv.tv_usec)) (interval.micros % 1_000_000); + return tv; + } + + /** + * Check the 'errno' global variable from the C standard library and + * throw an exception with the description of the error. + * + * Params: + * file = name of the source file where the check is being made; you + * would normally use __FILE__ for this parameter. + * line = line number of the source file where this method was called; + * you would normally use __LINE__ for this parameter. + * + * Throws: + * RegisteredConduitException when the conduit should not be registered + * but it is (EEXIST); UnregisteredConduitException when the conduit + * should be registered but it isn't (ENOENT); + * InterruptedSystemCallException when a system call has been interrupted + * (EINTR); OutOfMemoryException if a memory allocation fails (ENOMEM); + * SelectorException for any of the other cases in which errno is not 0. + */ + protected void checkErrno(char[] file, size_t line) + { + int errorCode = errno; + switch (errorCode) + { + case EBADF: + throw new SelectorException("Bad file descriptor", file, line); + // break; + case EEXIST: + throw new RegisteredConduitException(file, line); + // break; + case EINTR: + throw new InterruptedSystemCallException(file, line); + // break; + case EINVAL: + throw new SelectorException("An invalid parameter was sent to a system call", file, line); + // break; + case ENFILE: + throw new SelectorException("Maximum number of open files reached", file, line); + // break; + case ENOENT: + throw new UnregisteredConduitException(file, line); + // break; + case ENOMEM: + throw new OutOfMemoryException(file, line); + // break; + case EPERM: + throw new SelectorException("The conduit cannot be used with this Selector", file, line); + // break; + default: + throw new SelectorException("Unknown Selector error: " ~ SysError.lookup(errorCode), file, line); + // break; + } + } +}