1 /*******************************************************************************
2   copyright:   Copyright (c) 2006 Juan Jose Comellas. All rights reserved
3   license:     BSD style: $(LICENSE)
4   author:      Juan Jose Comellas $(EMAIL juanjo@comellas.com.ar)
5 *******************************************************************************/
6 
7 module tango.io.selector.model.ISelector;
8 
9 
10 public import tango.time.Time;
11 
12 public import tango.io.model.IConduit;
13 
14 /**
15  * Events that are used to register a Conduit to a selector and are returned
16  * in a SelectionKey after calling ISelector.select().
17  */
18 enum Event: uint
19 {
20     None            = 0,        // No event
21     // IMPORTANT: Do not change the values of the following symbols. They were
22     //            set in this way to map the values returned by the POSIX poll()
23     //            system call.
24     Read            = (1 << 0), // POLLIN
25     UrgentRead      = (1 << 1), // POLLPRI
26     Write           = (1 << 2), // POLLOUT
27     // The following events should not be used when registering a conduit to a
28     // selector. They are only used when returning events to the user.
29     Error           = (1 << 3), // POLLERR
30     Hangup          = (1 << 4), // POLLHUP
31     InvalidHandle   = (1 << 5)  // POLLNVAL
32 }
33 
34 
35 /**
36  * The SelectionKey struct holds the information concerning the conduits and
37  * their association to a selector. Each key keeps a reference to a registered
38  * conduit and the events that are to be tracked for it. The 'events' member
39  * of the key can take two meanings, depending on where it's used. If used
40  * with the register() method of the selector it represents the events we want
41  * to track; if used within a foreach cycle on an ISelectionSet it represents
42  * the events that have been detected for a conduit.
43  *
44  * The SelectionKey can also hold an optional object via the 'attachment'
45  * member. This member is very convenient to keep application-specific data
46  * that will be needed when the tracked events are triggered.
47  *
48  * See_also: $(SYMLINK ISelector, ISelector),
49  *           $(SYMLINK ISelectionSet, ISelectionSet)
50  */
51 struct SelectionKey
52 {
53     /**
54      * The conduit referred to by the SelectionKey.
55      */
56     ISelectable conduit;
57 
58     /**
59      * The registered (or selected) events as a bit mask of different Event
60      * values.
61      */
62     Event events;
63 
64     /**
65      * The attached Object referred to by the SelectionKey.
66      */
67     Object attachment;
68 
69     /**
70      * Check if a Read event has been associated to this SelectionKey.
71      */
72     public bool isReadable()
73     {
74         return ((events & Event.Read) != 0);
75     }
76 
77     /**
78      * Check if an UrgentRead event has been associated to this SelectionKey.
79      */
80     public bool isUrgentRead()
81     {
82         return ((events & Event.UrgentRead) != 0);
83     }
84 
85     /**
86      * Check if a Write event has been associated to this SelectionKey.
87      */
88     public bool isWritable()
89     {
90         return ((events & Event.Write) != 0);
91     }
92 
93     /**
94      * Check if an Error event has been associated to this SelectionKey.
95      */
96     public bool isError()
97     {
98         return ((events & Event.Error) != 0);
99     }
100 
101     /**
102      * Check if a Hangup event has been associated to this SelectionKey.
103      */
104     public bool isHangup()
105     {
106         return ((events & Event.Hangup) != 0);
107     }
108 
109     /**
110      * Check if an InvalidHandle event has been associated to this SelectionKey.
111      */
112     public bool isInvalidHandle()
113     {
114         return ((events & Event.InvalidHandle) != 0);
115     }
116 }
117 
118 
119 /**
120  * Container that holds the SelectionKey's for all the conduits that have
121  * triggered events during a previous invocation to ISelector.select().
122  * Instances of this container are normally returned from calls to
123  * ISelector.selectedSet().
124  */
125 interface ISelectionSet
126 {
127     /**
128      * Returns the number of SelectionKey's in the set.
129      */
130     public abstract size_t length();
131 
132     /**
133      * Operator to iterate over a set via a foreach block.  Note that any
134      * modifications to the SelectionKey will be ignored.
135      */
136     public abstract int opApply(scope int delegate(ref SelectionKey) dg);
137 }
138 
139 
140 /**
141  * A selector is a multiplexor for I/O events associated to a Conduit.
142  * All selectors must implement this interface.
143  *
144  * A selector needs to be initialized by calling the open() method to pass
145  * it the initial amount of conduits that it will handle and the maximum
146  * amount of events that will be returned per call to select(). In both cases,
147  * these values are only hints and may not even be used by the specific
148  * ISelector implementation you choose to use, so you cannot make any
149  * assumptions regarding what results from the call to select() (i.e. you
150  * may receive more or less events per call to select() than what was passed
151  * in the 'maxEvents' argument. The amount of conduits that the selector can
152  * manage will be incremented dynamically if necessary.
153  *
154  * To add or modify conduit registrations in the selector, use the register()
155  * method.  To remove conduit registrations from the selector, use the
156  * unregister() method.
157  *
158  * To wait for events from the conduits you need to call any of the select()
159  * methods. The selector cannot be modified from another thread while
160  * blocking on a call to these methods.
161  *
162  * Once the selector is no longer used you must call the close() method so
163  * that the selector can free any resources it may have allocated in the call
164  * to open().
165  *
166  * Examples:
167  * ---
168  * import tango.io.selector.model.ISelector;
169  * import tango.io.SocketConduit;
170  * import tango.io.Stdout;
171  *
172  * ISelector selector;
173  * SocketConduit conduit1;
174  * SocketConduit conduit2;
175  * MyClass object1;
176  * MyClass object2;
177  * int eventCount;
178  *
179  * // Initialize the selector assuming that it will deal with 2 conduits and
180  * // will receive 2 events per invocation to the select() method.
181  * selector.open(2, 2);
182  *
183  * selector.register(conduit, Event.Read, object1);
184  * selector.register(conduit, Event.Write, object2);
185  *
186  * eventCount = selector.select();
187  *
188  * if (eventCount > 0)
189  * {
190  *     char[16] buffer;
191  *     int count;
192  *
193  *     foreach (SelectionKey key, selector.selectedSet())
194  *     {
195  *         if (key.isReadable())
196  *         {
197  *             count = (cast(SocketConduit) key.conduit).read(buffer);
198  *             if (count != IConduit.Eof)
199  *             {
200  *                 Stdout.format("Received '{0}' from peer\n", buffer[0..count]);
201  *                 selector.register(key.conduit, Event.Write, key.attachment);
202  *             }
203  *             else
204  *             {
205  *                 selector.unregister(key.conduit);
206  *                 key.conduit.close();
207  *             }
208  *         }
209  *
210  *         if (key.isWritable())
211  *         {
212  *             count = (cast(SocketConduit) key.conduit).write("MESSAGE");
213  *             if (count != IConduit.Eof)
214  *             {
215  *                 Stdout.print("Sent 'MESSAGE' to peer\n");
216  *                 selector.register(key.conduit, Event.Read, key.attachment);
217  *             }
218  *             else
219  *             {
220  *                 selector.unregister(key.conduit);
221  *                 key.conduit.close();
222  *             }
223  *         }
224  *
225  *         if (key.isError() || key.isHangup() || key.isInvalidHandle())
226  *         {
227  *             selector.unregister(key.conduit);
228  *             key.conduit.close();
229  *         }
230  *     }
231  * }
232  *
233  * selector.close();
234  * ---
235  */
236 interface ISelector
237 {
238     /**
239      * Initialize the selector.
240      *
241      * Params:
242      * size         = value that provides a hint for the maximum amount of
243      *                conduits that will be registered
244      * maxEvents    = value that provides a hint for the maximum amount of
245      *                conduit events that will be returned in the selection
246      *                set per call to select.
247      */
248     public abstract void open(uint size, uint maxEvents);
249 
250     /**
251      * Free any operating system resources that may have been allocated in the
252      * call to open().
253      *
254      * Remarks:
255      * Not all of the selectors need to free resources other than allocated
256      * memory, but those that do will normally also add a call to close() in
257      * their destructors.
258      */
259     public abstract void close();
260 
261     /**
262      * Associate a conduit to the selector and track specific I/O events.
263      * If the conduit is already part of the selector, modify the events or
264      * atachment.
265      *
266      * Params:
267      * conduit      = conduit that will be associated to the selector;
268      *                must be a valid conduit (i.e. not null and open).
269      * events       = bit mask of Event values that represent the events that
270      *                will be tracked for the conduit.
271      * attachment   = optional object with application-specific data that will
272      *                be available when an event is triggered for the conduit
273      *
274      * Examples:
275      * ---
276      * ISelector selector;
277      * SocketConduit conduit;
278      * MyClass object;
279      *
280      * selector.register(conduit, Event.Read | Event.Write, object);
281      * ---
282      */
283     public abstract void register(ISelectable conduit, Event events,
284                                   Object attachment = null);
285 
286 
287     /**
288      * Deprecated, use register instead
289      */
290     deprecated public abstract void reregister(ISelectable conduit, Event
291             events, Object attachment = null);
292 
293     /**
294      * Remove a conduit from the selector.
295      *
296      * Params:
297      * conduit      = conduit that had been previously associated to the
298      *                selector; it can be null.
299      *
300      * Remarks:
301      * Unregistering a null conduit is allowed and no exception is thrown
302      * if this happens.
303      */
304     public abstract void unregister(ISelectable conduit);
305 
306 
307     /**
308      * Wait indefinitely for I/O events from the registered conduits.
309      *
310      * Returns:
311      * The amount of conduits that have received events; 0 if no conduits
312      * have received events within the specified timeout and -1 if there
313      * was an error.
314      */
315     public abstract int select();
316 
317     /**
318      * Wait for I/O events from the registered conduits for a specified
319      * amount of time.
320      *
321      * Params:
322      * timeout  = TimeSpan with the maximum amount of time that the
323      *            selector will wait for events from the conduits; the
324      *            amount of time is relative to the current system time
325      *            (i.e. just the number of milliseconds that the selector
326      *            has to wait for the events).
327      *
328      * Returns:
329      * The amount of conduits that have received events; 0 if no conduits
330      * have received events within the specified timeout.
331      */
332     public abstract int select(TimeSpan timeout);
333 
334     /**
335      * Wait for I/O events from the registered conduits for a specified
336      * amount of time.
337      *
338      * Note: This representation of timeout is not always accurate, so it is
339      * possible that the function will return with a timeout before the
340      * specified period.  For more accuracy, use the TimeSpan version.
341      *
342      * Note: Implementers should define this method as:
343      * -------
344      * select(TimeSpan.interval(timeout));
345      * -------
346      *
347      * Params:
348      * timeout  = the maximum amount of time in seconds that the
349      *            selector will wait for events from the conduits; the
350      *            amount of time is relative to the current system time
351      *            (i.e. just the number of milliseconds that the selector
352      *            has to wait for the events).
353      *
354      * Returns:
355      * The amount of conduits that have received events; 0 if no conduits
356      * have received events within the specified timeout.
357      */
358     public abstract int select(double timeout);
359 
360     /**
361      * Return the selection set resulting from the call to any of the select()
362      * methods.
363      *
364      * Remarks:
365      * If the call to select() was unsuccessful or it did not return any
366      * events, the returned value will be null.
367      */
368     public abstract ISelectionSet selectedSet();
369 
370     /**
371      * Return the selection key resulting from the registration of a conduit
372      * to the selector.
373      *
374      * Remarks:
375      * If the conduit is not registered to the selector the returned
376      * value will SelectionKey.init. No exception will be thrown by this
377      * method.
378      */
379     public abstract SelectionKey key(ISelectable conduit);
380 
381     /**
382      * Iterate through the currently registered selection keys.  Note that you
383      * should not erase or add any items from the selector while iterating,
384      * although you can register existing conduits again.
385      */
386     public abstract int opApply(scope int delegate(ref SelectionKey sk) dg);
387 }