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.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 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().
*
* See_Also: ISelector
*
* Examples:
* ---
* import tango.io.selector.model.ISelector;
* import tango.net.device.Socket;
* import tango.io.Stdout;
*
* 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.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("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();
* ---
*/
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);
/**
* Deprecated, use register instead
*/
deprecated public final void reregister(ISelectable conduit, Event events,
Object attachment = null)
{
register(conduit, events, 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.fromInterval(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);
/**
* Return the number of keys resulting from the registration of a conduit
* to the selector.
*/
public abstract size_t count();
/**
* 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.
*
* $(UL
* $(LI $(B RegisteredConduitException) when the conduit
* should not be registered but it is (EEXIST).)
* $(LI $(B UnregisteredConduitException) when the conduit
* should be registered but it isn't (ENOENT).)
* $(LI $(B InterruptedSystemCallException) when a system call has been
* interrupted (EINTR).)
* $(LI $(B OutOfMemoryException) if a memory allocation fails (ENOMEM).)
* $(LI $(B SelectorException) for any of the other cases
* in which errno is not 0.))
*/
protected void checkErrno(string 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).idup, file, line);
// break;
}
}
}
|