123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
|
/*******************************************************************************
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.PollSelector;
version (Posix)
{
public import tango.io.model.IConduit;
public import tango.io.selector.model.ISelector;
private import tango.io.selector.AbstractSelector;
private import tango.io.selector.SelectorException;
private import tango.sys.Common;
private import tango.stdc.errno;
version (linux)
private import tango.sys.linux.linux;
debug (selector)
private import tango.io.Stdout;
/**
* Selector that uses the poll() system call to receive I/O events for
* the registered conduits. To use this class you would normally do
* something like this:
*
* Examples:
* ---
* import tango.io.selector.PollSelector;
*
* Socket socket;
* ISelector selector = new PollSelector();
*
* selector.open(100, 10);
*
* // Register to read from socket
* selector.register(socket, Event.Read);
*
* int eventCount = selector.select(0.1); // 0.1 seconds
* if (eventCount > 0)
* {
* // We can now read from the socket
* socket.read();
* }
* else if (eventCount == 0)
* {
* // Timeout
* }
* else if (eventCount == -1)
* {
* // Another thread called the wakeup() method.
* }
* else
* {
* // Error: should never happen.
* }
*
* selector.close();
* ---
*/
public class PollSelector: 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
* PollSelector.
*/
public const uint DefaultSize = 64;
/** Map to associate the conduit handles with their selection keys */
private PollSelectionKey[ISelectable.Handle] _keys;
//private SelectionKey[] _selectedKeys;
private pollfd[] _pfds;
private uint _count = 0;
private int _eventCount = 0;
/**
* Open the poll()-based selector.
*
* 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 value is currently not used by this selector.
*/
override public void open(uint size = DefaultSize, uint maxEvents = DefaultSize)
in
{
assert(size > 0);
}
body
{
_pfds = new pollfd[size];
}
/**
* Close the selector.
*
* Remarks:
* It can be called multiple times without harmful side-effects.
*/
override public void close()
{
_keys = null;
//_selectedKeys = null;
_pfds = null;
_count = 0;
_eventCount = 0;
}
/**
* Associate a conduit to the selector and track specific I/O events.
* If a conduit is already associated, modify the events and
* attachment.
*
* 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.
*
* Examples:
* ---
* selector.register(conduit, Event.Read | Event.Write, object);
* ---
*/
override public void register(ISelectable conduit, Event events, Object attachment = null)
in
{
assert(conduit !is null && conduit.fileHandle() >= 0);
}
body
{
debug (selector)
Stdout.formatln("--- PollSelector.register(handle={0}, events=0x{1:x})",
cast(int) conduit.fileHandle(), cast(uint) events);
PollSelectionKey* current = (conduit.fileHandle() in _keys);
if (current !is null)
{
debug (selector)
Stdout.formatln("--- Adding pollfd in index {0} (of {1})",
current.index, _count);
current.key.events = events;
current.key.attachment = attachment;
_pfds[current.index].events = cast(short) events;
}
else
{
if (_count == _pfds.length)
_pfds.length = _pfds.length + 1;
_pfds[_count].fd = conduit.fileHandle();
_pfds[_count].events = cast(short) events;
_pfds[_count].revents = 0;
_keys[conduit.fileHandle()] = new PollSelectionKey(conduit, events, _count, attachment);
_count++;
}
}
/**
* 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.
*/
override public void unregister(ISelectable conduit)
{
if (conduit !is null)
{
try
{
debug (selector)
Stdout.formatln("--- PollSelector.unregister(handle={0})",
cast(int) conduit.fileHandle());
PollSelectionKey* removed = (conduit.fileHandle() in _keys);
if (removed !is null)
{
debug (selector)
Stdout.formatln("--- Removing pollfd in index {0} (of {1})",
removed.index, _count);
//
// instead of doing an O(n) remove, move the last
// element to the removed slot
//
_pfds[removed.index] = _pfds[_count - 1];
_keys[cast(ISelectable.Handle)_pfds[removed.index].fd].index = removed.index;
_count--;
_keys.remove(conduit.fileHandle());
}
else
{
debug (selector)
Stdout.formatln("--- PollSelector.unregister(handle={0}): conduit was not found",
cast(int) conduit.fileHandle());
throw new UnregisteredConduitException(__FILE__, __LINE__);
}
}
catch (Exception e)
{
debug (selector)
Stdout.formatln("--- Exception inside PollSelector.unregister(handle={0}): {1}",
cast(int) conduit.fileHandle(), e.toString());
throw new UnregisteredConduitException(__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.
*/
override public int select(TimeSpan timeout)
{
int to = (timeout != TimeSpan.max ? cast(int) timeout.millis : -1);
debug (selector)
Stdout.formatln("--- PollSelector.select({0} ms): waiting on {1} handles",
to, _count);
// We run the call to poll() inside a loop in case the system call
// was interrupted by a signal and we need to restart it.
while (true)
{
_eventCount = poll(_pfds.ptr, _count, to);
if (_eventCount < 0)
{
if (errno != EINTR || !_restartInterruptedSystemCall)
{
// The call to checkErrno() ends up throwing an exception
checkErrno(__FILE__, __LINE__);
}
debug (selector)
Stdout.print("--- Restarting poll() after being interrupted\n");
}
else
{
// Timeout or got a selection.
break;
}
}
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.
*/
override public ISelectionSet selectedSet()
{
return (_eventCount > 0 ? new PollSelectionSet(_pfds, _eventCount, _keys) : 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 SelectionKey.init. No exception will be thrown by this
* method.
*/
override public SelectionKey key(ISelectable conduit)
{
if(conduit !is null)
{
if(auto k = (conduit.fileHandle in _keys))
{
return k.key;
}
}
return SelectionKey.init;
}
/**
* Return the number of keys resulting from the registration of a conduit
* to the selector.
*/
override public size_t count()
{
return _keys.length;
}
/**
* 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 int opApply(scope int delegate(ref SelectionKey sk) dg)
{
int result = 0;
foreach(k; _keys)
{
SelectionKey sk = k.key;
if((result = dg(sk)) != 0)
break;
}
return result;
}
unittest
{
}
}
/**
* Class used to hold the list of Conduits that have received events.
*/
private class PollSelectionSet: ISelectionSet
{
pollfd[] fds;
int numSelected;
PollSelectionKey[ISelectable.Handle] keys;
this(pollfd[] fds, int numSelected, PollSelectionKey[ISelectable.Handle] keys)
{
this.fds = fds;
this.numSelected = numSelected;
this.keys = keys;
}
size_t length()
{
return numSelected;
}
/**
* Iterate over all the Conduits that have received events.
*/
int opApply(scope int delegate(ref SelectionKey) dg)
{
int rc = 0;
int nLeft = numSelected;
foreach (pfd; fds)
{
//
// see if the revent is set
//
if(pfd.revents != 0)
{
debug (selector)
Stdout.formatln("--- Found events 0x{0:x} for handle {1}",
cast(uint) pfd.revents, cast(int) pfd.fd);
auto k = (cast(ISelectable.Handle)pfd.fd) in keys;
if(k !is null)
{
SelectionKey current = k.key;
current.events = cast(Event)pfd.revents;
if ((rc = dg(current)) != 0)
{
break;
}
}
else
{
debug (selector)
Stdout.formatln("--- Handle {0} was not found in the Selector",
cast(int) pfd.fd);
}
if(--nLeft == 0)
break;
}
}
return rc;
}
}
/**
* Class that holds the information that the PollSelector needs to deal
* with each registered Conduit.
*/
private class PollSelectionKey
{
SelectionKey key;
uint index;
public this()
{
}
public this(ISelectable conduit, Event events, uint index, Object attachment)
{
this.key = SelectionKey(conduit, events, attachment);
this.index = index;
}
}
}
|