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;
+        }
+    }
+}