| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 | /******************************************************************************* copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved license: BSD style: $(LICENSE) author: Juan Jose Comellas $(EMAIL juanjo@comellas.com.ar) *******************************************************************************/ module tango.io.selector.model.ISelector; public import tango.time.Time; public import tango.io.model.IConduit; /** * Events that are used to register a Conduit to a selector and are returned * in a SelectionKey after calling ISelector.select(). */ enum Event: uint { None = 0, // No event // IMPORTANT: Do not change the values of the following symbols. They were // set in this way to map the values returned by the POSIX poll() // system call. Read = (1 << 0), // POLLIN UrgentRead = (1 << 1), // POLLPRI Write = (1 << 2), // POLLOUT // The following events should not be used when registering a conduit to a // selector. They are only used when returning events to the user. Error = (1 << 3), // POLLERR Hangup = (1 << 4), // POLLHUP InvalidHandle = (1 << 5) // POLLNVAL } /** * The SelectionKey struct holds the information concerning the conduits and * their association to a selector. Each key keeps a reference to a registered * conduit and the events that are to be tracked for it. The 'events' member * of the key can take two meanings, depending on where it's used. If used * with the register() method of the selector it represents the events we want * to track; if used within a foreach cycle on an ISelectionSet it represents * the events that have been detected for a conduit. * * The SelectionKey can also hold an optional object via the 'attachment' * member. This member is very convenient to keep application-specific data * that will be needed when the tracked events are triggered. * * See_also: $(SYMLINK ISelector, ISelector), * $(SYMLINK ISelectionSet, ISelectionSet) */ struct SelectionKey { /** * The conduit referred to by the SelectionKey. */ ISelectable conduit; /** * The registered (or selected) events as a bit mask of different Event * values. */ Event events; /** * The attached Object referred to by the SelectionKey. */ Object attachment; /** * Check if a Read event has been associated to this SelectionKey. */ public bool isReadable() { return ((events & Event.Read) != 0); } /** * Check if an UrgentRead event has been associated to this SelectionKey. */ public bool isUrgentRead() { return ((events & Event.UrgentRead) != 0); } /** * Check if a Write event has been associated to this SelectionKey. */ public bool isWritable() { return ((events & Event.Write) != 0); } /** * Check if an Error event has been associated to this SelectionKey. */ public bool isError() { return ((events & Event.Error) != 0); } /** * Check if a Hangup event has been associated to this SelectionKey. */ public bool isHangup() { return ((events & Event.Hangup) != 0); } /** * Check if an InvalidHandle event has been associated to this SelectionKey. */ public bool isInvalidHandle() { return ((events & Event.InvalidHandle) != 0); } } /** * Container that holds the SelectionKey's for all the conduits that have * triggered events during a previous invocation to ISelector.select(). * Instances of this container are normally returned from calls to * ISelector.selectedSet(). */ interface ISelectionSet { /** * Returns the number of SelectionKey's in the set. */ public abstract size_t length(); /** * Operator to iterate over a set via a foreach block. Note that any * modifications to the SelectionKey will be ignored. */ public abstract int opApply(scope int delegate(ref SelectionKey) dg); } /** * 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 or modify conduit registrations in the selector, use the register() * method. To remove conduit registrations from the selector, use the * unregister() method. * * 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(). * * Examples: * --- * import tango.io.selector.model.ISelector; * import tango.io.SocketConduit; * import tango.io.Stdout; * * ISelector selector; * SocketConduit conduit1; * SocketConduit conduit2; * MyClass object1; * MyClass object2; * int 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.register(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.print("Sent 'MESSAGE' to peer\n"); * selector.register(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(); * --- */ interface ISelector { /** * 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. * If the conduit is already part of the selector, modify the events or * atachment. * * Params: * conduit = conduit that will be associated to the selector; * must be a valid conduit (i.e. not null and open). * 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: * --- * ISelector selector; * SocketConduit conduit; * MyClass object; * * selector.register(conduit, Event.Read | Event.Write, object); * --- */ public abstract void register(ISelectable conduit, Event events, Object attachment = null); /** * Deprecated, use register instead */ deprecated public abstract void reregister(ISelectable conduit, Event events, Object attachment = null); /** * 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 indefinitely for I/O events from the registered conduits. * * Returns: * The amount of conduits that have received events; 0 if no conduits * have received events within the specified timeout and -1 if there * was an error. */ public abstract int select(); /** * 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. */ public abstract int select(TimeSpan timeout); /** * 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. * * Note: Implementers should define this method as: * ------- * select(TimeSpan.interval(timeout)); * ------- * * 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 abstract int select(double timeout); /** * 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 SelectionKey.init. No exception will be thrown by this * method. */ public abstract SelectionKey key(ISelectable conduit); /** * Iterate through the currently registered selection keys. Note that you * should not erase or add any items from the selector while iterating, * although you can register existing conduits again. */ public abstract int opApply(scope int delegate(ref SelectionKey sk) dg); } |