Mercurial > projects > ldc
diff tango/tango/io/selector/EpollSelector.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/EpollSelector.d Fri Jan 11 17:57:40 2008 +0100 @@ -0,0 +1,465 @@ +/******************************************************************************* + 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.EpollSelector; + + +version (linux) +{ + public import tango.io.model.IConduit; + + private import tango.io.selector.model.ISelector; + private import tango.io.selector.AbstractSelector; + private import tango.sys.Common; + private import tango.sys.linux.linux; + private import tango.stdc.errno; + + debug (selector) + private import tango.io.Stdout; + + + /** + * Selector that uses the Linux epoll* family of system calls. + * + * This selector is the best option when dealing with large amounts of + * conduits under Linux. It will scale much better than any of the other + * options (PollSelector, SelectSelector). For small amounts of conduits + * (n < 20) the PollSelector will probably be more performant. + * + * See_Also: ISelector, AbstractSelector + * + * Examples: + * --- + * import tango.io.selector.EpollSelector; + * import tango.io.Stdout; + * import tango.net.SocketConduit; + * + * SocketConduit conduit1; + * SocketConduit conduit2; + * EpollSelector selector = new EpollSelector(); + * MyClass object1 = new MyClass(); + * MyClass object2 = new MyClass(); + * uint eventCount; + * + * // Initialize the selector assuming that it will deal with 10 conduits and + * // will receive 3 events per invocation to the select() method. + * selector.open(10, 3); + * + * selector.register(conduit1, Event.Read, object1); + * selector.register(conduit2, 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"); + * 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(); + * --- + */ + public class EpollSelector: AbstractSelector + { + /** + * Alias for the select() method as we're not reimplementing it in + * this class. + */ + alias AbstractSelector.select select; + + /** + * Default number of SelectionKey's that will be handled by the + * EpollSelector. + */ + public const uint DefaultSize = 64; + /** + * Default maximum number of events that will be received per + * invocation to select(). + */ + public const uint DefaultMaxEvents = 16; + + + /** Map to associate the conduit handles with their selection keys */ + private SelectionKey[ISelectable.Handle] _keys; + /** File descriptor returned by the epoll_create() system call. */ + private int _epfd = -1; + /** + * Array of events that is filled by epoll_wait() inside the call + * to select(). + */ + private epoll_event[] _events; + /** Number of events resulting from the call to select() */ + private int _eventCount = 0; + + + /** + * Destructor + */ + ~this() + { + // Make sure that we release the epoll file descriptor once this + // object is garbage collected. + close(); + } + + /** + * Open the epoll selector, makes a call to epoll_create() + * + * Params: + * size = maximum amount of conduits that will be registered; + * it will grow dynamically if needed. + * maxEvents = maximum amount of conduit events that will be + * returned in the selection set per call to select(); + * this limit is enforced by this selector. + * + * Throws: + * SelectorException if there are not enough resources to open the + * selector (e.g. not enough file handles or memory available). + */ + public void open(uint size = DefaultSize, uint maxEvents = DefaultMaxEvents) + in + { + assert(size > 0); + assert(maxEvents > 0); + } + body + { + _events = new epoll_event[maxEvents]; + + _epfd = epoll_create(cast(int) size); + if (_epfd < 0) + { + checkErrno(__FILE__, __LINE__); + } + } + + /** + * Close the selector, releasing the file descriptor that had been + * created in the previous call to open(). + * + * Remarks: + * It can be called multiple times without harmful side-effects. + */ + public void close() + { + if (_epfd >= 0) + { + .close(_epfd); + _epfd = -1; + } + _events = null; + _eventCount = 0; + } + + /** + * Associate a conduit to the selector and track specific I/O events. + * + * 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 + * + * Throws: + * RegisteredConduitException if the conduit had already been + * registered to the selector; SelectorException if there are not + * enough resources to add the conduit to the selector. + * + * Examples: + * --- + * selector.register(conduit, Event.Read | Event.Write, object); + * --- + */ + public void register(ISelectable conduit, Event events, Object attachment = null) + in + { + assert(conduit !is null && conduit.fileHandle() >= 0); + } + body + { + epoll_event event; + SelectionKey key = new SelectionKey(conduit, events, attachment); + + event.events = events; + // We associate the selection key to the epoll_event to be able to + // retrieve it efficiently when we get events for this handle. + event.data.ptr = cast(void*) key; + + if (epoll_ctl(_epfd, EPOLL_CTL_ADD, conduit.fileHandle(), &event) == 0) + { + // We keep the keys in a map to make sure that the key is not + // garbage collected while there is still a reference to it in + // an epoll_event. This also allows to to efficiently find the + // key corresponding to a handle in methods where this + // association is not provided automatically. + _keys[conduit.fileHandle()] = key; + } + else + { + checkErrno(__FILE__, __LINE__); + } + } + + /** + * 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; + * 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 + * + * Remarks: + * The 'attachment' member of the SelectionKey will always be + * overwritten, even if it's null. + * + * Throws: + * UnregisteredConduitException if the conduit had not been previously + * registered to the selector; SelectorException if there are not + * enough resources to modify the conduit registration. + * + * Examples: + * --- + * selector.reregister(conduit, Event.Write, object); + * --- + */ + public void reregister(ISelectable conduit, Event events, Object attachment = null) + in + { + assert(conduit !is null && conduit.fileHandle() >= 0); + } + body + { + SelectionKey* key = (conduit.fileHandle() in _keys); + + if (key !is null) + { + epoll_event event; + + (*key).events = events; + (*key).attachment = attachment; + + event.events = events; + event.data.ptr = cast(void*) *key; + + if (epoll_ctl(_epfd, EPOLL_CTL_MOD, conduit.fileHandle(), &event) != 0) + { + checkErrno(__FILE__, __LINE__); + } + } + else + { + throw new UnregisteredConduitException(__FILE__, __LINE__); + } + } + + /** + * 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. + * + * Throws: + * UnregisteredConduitException if the conduit had not been previously + * registered to the selector; SelectorException if there are not + * enough resources to remove the conduit registration. + */ + public void unregister(ISelectable conduit) + { + if (conduit !is null) + { + if (epoll_ctl(_epfd, EPOLL_CTL_DEL, conduit.fileHandle(), null) == 0) + { + _keys.remove(conduit.fileHandle()); + } + else + { + checkErrno(__FILE__, __LINE__); + } + } + } + + /** + * 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. + * + * Throws: + * InterruptedSystemCallException if the underlying system call was + * interrupted by a signal and the 'restartInterruptedSystemCall' + * property was set to false; SelectorException if there were no + * resources available to wait for events from the conduits. + */ + public int select(TimeSpan timeout) + { + int to = (timeout != TimeSpan.max ? cast(int) timeout.millis : -1); + + while (true) + { + // FIXME: add support for the wakeup() call. + _eventCount = epoll_wait(_epfd, _events.ptr, _events.length, to); + if (_eventCount >= 0) + { + break; + } + else + { + if (errno != EINTR || !_restartInterruptedSystemCall) + { + checkErrno(__FILE__, __LINE__); + } + debug (selector) + Stdout("--- Restarting epoll_wait() after being interrupted by a signal\n"); + } + } + return _eventCount; + } + + /** + * 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 ISelectionSet selectedSet() + { + return (_eventCount > 0 ? new EpollSelectionSet(_events[0.._eventCount]) : null); + } + + /** + * 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 SelectionKey key(ISelectable conduit) + { + return (conduit !is null ? _keys[conduit.fileHandle()] : null); + } + + unittest + { + } + } + + /** + * Class used to hold the list of Conduits that have received events. + */ + private class EpollSelectionSet: ISelectionSet + { + private epoll_event[] _events; + + protected this(epoll_event[] events) + { + _events = events; + } + + public uint length() + { + return _events.length; + } + + /** + * Iterate over all the Conduits that have received events. + */ + public int opApply(int delegate(inout SelectionKey) dg) + { + int rc = 0; + SelectionKey key; + + debug (selector) + Stdout.format("--- EpollSelectionSet.opApply() ({0} events)\n", _events.length); + + foreach (epoll_event event; _events) + { + // Only invoke the delegate if there is an event for the conduit. + if (event.events != 0) + { + key = cast(SelectionKey) event.data.ptr; + key.events = cast(Event) event.events; + + debug (selector) + Stdout.format("--- Event 0x{0:x} for handle {1}\n", + cast(uint) event.events, cast(int) key.conduit.fileHandle()); + + rc = dg(key); + if (rc != 0) + { + break; + } + } + } + return rc; + } + } +} +