Mercurial > projects > ldc
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 } |