comparison 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
comparison
equal deleted inserted replaced
131:5825d48b27d1 132:1700239cab2e
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 }