132
|
1 /*******************************************************************************
|
|
2 copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
|
|
3 license: BSD style: $(LICENSE)
|
|
4 author: Juan Jose Comellas <juanjo@comellas.com.ar>
|
|
5 *******************************************************************************/
|
|
6
|
|
7 module tango.io.selector.AbstractSelector;
|
|
8
|
|
9 public import tango.io.model.IConduit;
|
|
10 public import tango.io.selector.SelectorException;
|
|
11
|
|
12 private import tango.io.selector.model.ISelector;
|
|
13 private import tango.sys.Common;
|
|
14 private import tango.stdc.errno;
|
|
15
|
|
16 version (Windows)
|
|
17 {
|
|
18 public struct timeval
|
|
19 {
|
|
20 int tv_sec; // seconds
|
|
21 int tv_usec; // microseconds
|
|
22 }
|
|
23 }
|
|
24
|
|
25 /**
|
|
26 * Base class for all selectors.
|
|
27 *
|
|
28 * A selector is a multiplexor for I/O events associated to a Conduit.
|
|
29 * All selectors must implement this interface.
|
|
30 *
|
|
31 * A selector needs to be initialized by calling the open() method to pass
|
|
32 * it the initial amount of conduits that it will handle and the maximum
|
|
33 * amount of events that will be returned per call to select(). In both cases,
|
|
34 * these values are only hints and may not even be used by the specific
|
|
35 * ISelector implementation you choose to use, so you cannot make any
|
|
36 * assumptions regarding what results from the call to select() (i.e. you
|
|
37 * may receive more or less events per call to select() than what was passed
|
|
38 * in the 'maxEvents' argument. The amount of conduits that the selector can
|
|
39 * manage will be incremented dynamically if necessary.
|
|
40 *
|
|
41 * To add, modify or remove conduit registrations to the selector you use
|
|
42 * the register(), reregister() and unregister() methods respectively.
|
|
43 *
|
|
44 * To wait for events from the conduits you need to call any of the select()
|
|
45 * methods. The selector cannot be modified from another thread while
|
|
46 * blocking on a call to these methods.
|
|
47 *
|
|
48 * Once the selector is no longer used you must call the close() method so
|
|
49 * that the selector can free any resources it may have allocated in the call
|
|
50 * to open().
|
|
51 *
|
|
52 * See_Also: ISelector
|
|
53 *
|
|
54 * Examples:
|
|
55 * ---
|
|
56 * import tango.io.selector.model.ISelector;
|
|
57 * import tango.io.Stdout;
|
|
58 * import tango.net.SocketConduit;
|
|
59 *
|
|
60 * AbstractSelector selector;
|
|
61 * SocketConduit conduit1;
|
|
62 * SocketConduit conduit2;
|
|
63 * MyClass object1;
|
|
64 * MyClass object2;
|
|
65 * uint eventCount;
|
|
66 *
|
|
67 * // Initialize the selector assuming that it will deal with 2 conduits and
|
|
68 * // will receive 2 events per invocation to the select() method.
|
|
69 * selector.open(2, 2);
|
|
70 *
|
|
71 * selector.register(conduit, Event.Read, object1);
|
|
72 * selector.register(conduit, Event.Write, object2);
|
|
73 *
|
|
74 * eventCount = selector.select();
|
|
75 *
|
|
76 * if (eventCount > 0)
|
|
77 * {
|
|
78 * char[16] buffer;
|
|
79 * int count;
|
|
80 *
|
|
81 * foreach (SelectionKey key, selector.selectedSet())
|
|
82 * {
|
|
83 * if (key.isReadable())
|
|
84 * {
|
|
85 * count = (cast(SocketConduit) key.conduit).read(buffer);
|
|
86 * if (count != IConduit.Eof)
|
|
87 * {
|
|
88 * Stdout.format("Received '{0}' from peer\n", buffer[0..count]);
|
|
89 * selector.reregister(key.conduit, Event.Write, key.attachment);
|
|
90 * }
|
|
91 * else
|
|
92 * {
|
|
93 * selector.unregister(key.conduit);
|
|
94 * key.conduit.close();
|
|
95 * }
|
|
96 * }
|
|
97 *
|
|
98 * if (key.isWritable())
|
|
99 * {
|
|
100 * count = (cast(SocketConduit) key.conduit).write("MESSAGE");
|
|
101 * if (count != IConduit.Eof)
|
|
102 * {
|
|
103 * Stdout("Sent 'MESSAGE' to peer\n");
|
|
104 * selector.reregister(key.conduit, Event.Read, key.attachment);
|
|
105 * }
|
|
106 * else
|
|
107 * {
|
|
108 * selector.unregister(key.conduit);
|
|
109 * key.conduit.close();
|
|
110 * }
|
|
111 * }
|
|
112 *
|
|
113 * if (key.isError() || key.isHangup() || key.isInvalidHandle())
|
|
114 * {
|
|
115 * selector.unregister(key.conduit);
|
|
116 * key.conduit.close();
|
|
117 * }
|
|
118 * }
|
|
119 * }
|
|
120 *
|
|
121 * selector.close();
|
|
122 * ---
|
|
123 */
|
|
124 abstract class AbstractSelector: ISelector
|
|
125 {
|
|
126 /**
|
|
127 * Restart interrupted system calls when blocking inside a call to select.
|
|
128 */
|
|
129 protected bool _restartInterruptedSystemCall = true;
|
|
130
|
|
131 /**
|
|
132 * Indicates whether interrupted system calls will be restarted when
|
|
133 * blocking inside a call to select.
|
|
134 */
|
|
135 public bool restartInterruptedSystemCall()
|
|
136 {
|
|
137 return _restartInterruptedSystemCall;
|
|
138 }
|
|
139
|
|
140 /**
|
|
141 * Sets whether interrupted system calls will be restarted when
|
|
142 * blocking inside a call to select.
|
|
143 */
|
|
144 public void restartInterruptedSystemCall(bool value)
|
|
145 {
|
|
146 _restartInterruptedSystemCall = value;
|
|
147 }
|
|
148
|
|
149 /**
|
|
150 * Initialize the selector.
|
|
151 *
|
|
152 * Params:
|
|
153 * size = value that provides a hint for the maximum amount of
|
|
154 * conduits that will be registered
|
|
155 * maxEvents = value that provides a hint for the maximum amount of
|
|
156 * conduit events that will be returned in the selection
|
|
157 * set per call to select.
|
|
158 */
|
|
159 public abstract void open(uint size, uint maxEvents);
|
|
160
|
|
161 /**
|
|
162 * Free any operating system resources that may have been allocated in the
|
|
163 * call to open().
|
|
164 *
|
|
165 * Remarks:
|
|
166 * Not all of the selectors need to free resources other than allocated
|
|
167 * memory, but those that do will normally also add a call to close() in
|
|
168 * their destructors.
|
|
169 */
|
|
170 public abstract void close();
|
|
171
|
|
172 /**
|
|
173 * Associate a conduit to the selector and track specific I/O events.
|
|
174 *
|
|
175 * Params:
|
|
176 * conduit = conduit that will be associated to the selector
|
|
177 * events = bit mask of Event values that represent the events that
|
|
178 * will be tracked for the conduit.
|
|
179 * attachment = optional object with application-specific data that will
|
|
180 * be available when an event is triggered for the conduit
|
|
181 *
|
|
182 * Examples:
|
|
183 * ---
|
|
184 * AbstractSelector selector;
|
|
185 * SocketConduit conduit;
|
|
186 * MyClass object;
|
|
187 *
|
|
188 * selector.register(conduit, Event.Read | Event.Write, object);
|
|
189 * ---
|
|
190 */
|
|
191 public abstract void register(ISelectable conduit, Event events,
|
|
192 Object attachment);
|
|
193
|
|
194 /**
|
|
195 * Modify the events that are being tracked or the 'attachment' field
|
|
196 * for an already registered conduit.
|
|
197 *
|
|
198 * Params:
|
|
199 * conduit = conduit that will be associated to the selector
|
|
200 * events = bit mask of Event values that represent the events that
|
|
201 * will be tracked for the conduit.
|
|
202 * attachment = optional object with application-specific data that will
|
|
203 * be available when an event is triggered for the conduit
|
|
204 *
|
|
205 * Remarks:
|
|
206 * The 'attachment' member of the SelectionKey will always be overwritten,
|
|
207 * even if it's null.
|
|
208 *
|
|
209 * Examples:
|
|
210 * ---
|
|
211 * AbstractSelector selector;
|
|
212 * SocketConduit conduit;
|
|
213 * MyClass object;
|
|
214 *
|
|
215 * selector.reregister(conduit, Event.Write, object);
|
|
216 * ---
|
|
217 */
|
|
218 public abstract void reregister(ISelectable conduit, Event events,
|
|
219 Object attachment);
|
|
220
|
|
221 /**
|
|
222 * Remove a conduit from the selector.
|
|
223 *
|
|
224 * Params:
|
|
225 * conduit = conduit that had been previously associated to the
|
|
226 * selector; it can be null.
|
|
227 *
|
|
228 * Remarks:
|
|
229 * Unregistering a null conduit is allowed and no exception is thrown
|
|
230 * if this happens.
|
|
231 */
|
|
232 public abstract void unregister(ISelectable conduit);
|
|
233
|
|
234 /**
|
|
235 * Wait for I/O events from the registered conduits for a specified
|
|
236 * amount of time.
|
|
237 *
|
|
238 * Returns:
|
|
239 * The amount of conduits that have received events; 0 if no conduits
|
|
240 * have received events within the specified timeout; and -1 if the
|
|
241 * wakeup() method has been called from another thread.
|
|
242 *
|
|
243 * Remarks:
|
|
244 * This method is the same as calling select(TimeSpan.max).
|
|
245 */
|
|
246 public int select()
|
|
247 {
|
|
248 return select(TimeSpan.max);
|
|
249 }
|
|
250
|
|
251 /**
|
|
252 * Wait for I/O events from the registered conduits for a specified
|
|
253 * amount of time.
|
|
254 *
|
|
255 * Note: This representation of timeout is not always accurate, so it is
|
|
256 * possible that the function will return with a timeout before the
|
|
257 * specified period. For more accuracy, use the TimeSpan version.
|
|
258 *
|
|
259 * Params:
|
|
260 * timeout = the maximum amount of time in seconds that the
|
|
261 * selector will wait for events from the conduits; the
|
|
262 * amount of time is relative to the current system time
|
|
263 * (i.e. just the number of milliseconds that the selector
|
|
264 * has to wait for the events).
|
|
265 *
|
|
266 * Returns:
|
|
267 * The amount of conduits that have received events; 0 if no conduits
|
|
268 * have received events within the specified timeout.
|
|
269 */
|
|
270 public int select(double timeout)
|
|
271 {
|
|
272 return select(TimeSpan.interval(timeout));
|
|
273 }
|
|
274
|
|
275 /**
|
|
276 * Wait for I/O events from the registered conduits for a specified
|
|
277 * amount of time.
|
|
278 *
|
|
279 * Params:
|
|
280 * timeout = TimeSpan with the maximum amount of time that the
|
|
281 * selector will wait for events from the conduits; the
|
|
282 * amount of time is relative to the current system time
|
|
283 * (i.e. just the number of milliseconds that the selector
|
|
284 * has to wait for the events).
|
|
285 *
|
|
286 * Returns:
|
|
287 * The amount of conduits that have received events; 0 if no conduits
|
|
288 * have received events within the specified timeout; and -1 if the
|
|
289 * wakeup() method has been called from another thread.
|
|
290 */
|
|
291 public abstract int select(TimeSpan timeout);
|
|
292
|
|
293 /**
|
|
294 * Causes the first call to select() that has not yet returned to return
|
|
295 * immediately.
|
|
296 *
|
|
297 * If another thread is currently blocked in an call to any of the
|
|
298 * select() methods then that call will return immediately. If no
|
|
299 * selection operation is currently in progress then the next invocation
|
|
300 * of one of these methods will return immediately. In any case the value
|
|
301 * returned by that invocation may be non-zero. Subsequent invocations of
|
|
302 * the select() methods will block as usual unless this method is invoked
|
|
303 * again in the meantime.
|
|
304 */
|
|
305 // public abstract void wakeup();
|
|
306
|
|
307 /**
|
|
308 * Return the selection set resulting from the call to any of the select()
|
|
309 * methods.
|
|
310 *
|
|
311 * Remarks:
|
|
312 * If the call to select() was unsuccessful or it did not return any
|
|
313 * events, the returned value will be null.
|
|
314 */
|
|
315 public abstract ISelectionSet selectedSet();
|
|
316
|
|
317 /**
|
|
318 * Return the selection key resulting from the registration of a conduit
|
|
319 * to the selector.
|
|
320 *
|
|
321 * Remarks:
|
|
322 * If the conduit is not registered to the selector the returned
|
|
323 * value will be null. No exception will be thrown by this method.
|
|
324 */
|
|
325 public abstract SelectionKey key(ISelectable conduit);
|
|
326
|
|
327 /**
|
|
328 * Cast the time duration to a C timeval struct.
|
|
329 */
|
|
330 public timeval* toTimeval(timeval* tv, TimeSpan interval)
|
|
331 in
|
|
332 {
|
|
333 assert(tv !is null);
|
|
334 }
|
|
335 body
|
|
336 {
|
|
337 tv.tv_sec = cast(typeof(tv.tv_sec)) interval.seconds;
|
|
338 tv.tv_usec = cast(typeof(tv.tv_usec)) (interval.micros % 1_000_000);
|
|
339 return tv;
|
|
340 }
|
|
341
|
|
342 /**
|
|
343 * Check the 'errno' global variable from the C standard library and
|
|
344 * throw an exception with the description of the error.
|
|
345 *
|
|
346 * Params:
|
|
347 * file = name of the source file where the check is being made; you
|
|
348 * would normally use __FILE__ for this parameter.
|
|
349 * line = line number of the source file where this method was called;
|
|
350 * you would normally use __LINE__ for this parameter.
|
|
351 *
|
|
352 * Throws:
|
|
353 * RegisteredConduitException when the conduit should not be registered
|
|
354 * but it is (EEXIST); UnregisteredConduitException when the conduit
|
|
355 * should be registered but it isn't (ENOENT);
|
|
356 * InterruptedSystemCallException when a system call has been interrupted
|
|
357 * (EINTR); OutOfMemoryException if a memory allocation fails (ENOMEM);
|
|
358 * SelectorException for any of the other cases in which errno is not 0.
|
|
359 */
|
|
360 protected void checkErrno(char[] file, size_t line)
|
|
361 {
|
|
362 int errorCode = errno;
|
|
363 switch (errorCode)
|
|
364 {
|
|
365 case EBADF:
|
|
366 throw new SelectorException("Bad file descriptor", file, line);
|
|
367 // break;
|
|
368 case EEXIST:
|
|
369 throw new RegisteredConduitException(file, line);
|
|
370 // break;
|
|
371 case EINTR:
|
|
372 throw new InterruptedSystemCallException(file, line);
|
|
373 // break;
|
|
374 case EINVAL:
|
|
375 throw new SelectorException("An invalid parameter was sent to a system call", file, line);
|
|
376 // break;
|
|
377 case ENFILE:
|
|
378 throw new SelectorException("Maximum number of open files reached", file, line);
|
|
379 // break;
|
|
380 case ENOENT:
|
|
381 throw new UnregisteredConduitException(file, line);
|
|
382 // break;
|
|
383 case ENOMEM:
|
|
384 throw new OutOfMemoryException(file, line);
|
|
385 // break;
|
|
386 case EPERM:
|
|
387 throw new SelectorException("The conduit cannot be used with this Selector", file, line);
|
|
388 // break;
|
|
389 default:
|
|
390 throw new SelectorException("Unknown Selector error: " ~ SysError.lookup(errorCode), file, line);
|
|
391 // break;
|
|
392 }
|
|
393 }
|
|
394 }
|