Mercurial > projects > ldc
comparison tango/tango/io/selector/EpollSelector.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.EpollSelector; | |
8 | |
9 | |
10 version (linux) | |
11 { | |
12 public import tango.io.model.IConduit; | |
13 | |
14 private import tango.io.selector.model.ISelector; | |
15 private import tango.io.selector.AbstractSelector; | |
16 private import tango.sys.Common; | |
17 private import tango.sys.linux.linux; | |
18 private import tango.stdc.errno; | |
19 | |
20 debug (selector) | |
21 private import tango.io.Stdout; | |
22 | |
23 | |
24 /** | |
25 * Selector that uses the Linux epoll* family of system calls. | |
26 * | |
27 * This selector is the best option when dealing with large amounts of | |
28 * conduits under Linux. It will scale much better than any of the other | |
29 * options (PollSelector, SelectSelector). For small amounts of conduits | |
30 * (n < 20) the PollSelector will probably be more performant. | |
31 * | |
32 * See_Also: ISelector, AbstractSelector | |
33 * | |
34 * Examples: | |
35 * --- | |
36 * import tango.io.selector.EpollSelector; | |
37 * import tango.io.Stdout; | |
38 * import tango.net.SocketConduit; | |
39 * | |
40 * SocketConduit conduit1; | |
41 * SocketConduit conduit2; | |
42 * EpollSelector selector = new EpollSelector(); | |
43 * MyClass object1 = new MyClass(); | |
44 * MyClass object2 = new MyClass(); | |
45 * uint eventCount; | |
46 * | |
47 * // Initialize the selector assuming that it will deal with 10 conduits and | |
48 * // will receive 3 events per invocation to the select() method. | |
49 * selector.open(10, 3); | |
50 * | |
51 * selector.register(conduit1, Event.Read, object1); | |
52 * selector.register(conduit2, Event.Write, object2); | |
53 * | |
54 * eventCount = selector.select(); | |
55 * | |
56 * if (eventCount > 0) | |
57 * { | |
58 * char[16] buffer; | |
59 * int count; | |
60 * | |
61 * foreach (SelectionKey key, selector.selectedSet()) | |
62 * { | |
63 * if (key.isReadable()) | |
64 * { | |
65 * count = (cast(SocketConduit) key.conduit).read(buffer); | |
66 * if (count != IConduit.Eof) | |
67 * { | |
68 * Stdout.format("Received '{0}' from peer\n", buffer[0..count]); | |
69 * selector.reregister(key.conduit, Event.Write, key.attachment); | |
70 * } | |
71 * else | |
72 * { | |
73 * selector.unregister(key.conduit); | |
74 * key.conduit.close(); | |
75 * } | |
76 * } | |
77 * | |
78 * if (key.isWritable()) | |
79 * { | |
80 * count = (cast(SocketConduit) key.conduit).write("MESSAGE"); | |
81 * if (count != IConduit.Eof) | |
82 * { | |
83 * Stdout("Sent 'MESSAGE' to peer"); | |
84 * selector.reregister(key.conduit, Event.Read, key.attachment); | |
85 * } | |
86 * else | |
87 * { | |
88 * selector.unregister(key.conduit); | |
89 * key.conduit.close(); | |
90 * } | |
91 * } | |
92 * | |
93 * if (key.isError() || key.isHangup() || key.isInvalidHandle()) | |
94 * { | |
95 * selector.unregister(key.conduit); | |
96 * key.conduit.close(); | |
97 * } | |
98 * } | |
99 * } | |
100 * | |
101 * selector.close(); | |
102 * --- | |
103 */ | |
104 public class EpollSelector: AbstractSelector | |
105 { | |
106 /** | |
107 * Alias for the select() method as we're not reimplementing it in | |
108 * this class. | |
109 */ | |
110 alias AbstractSelector.select select; | |
111 | |
112 /** | |
113 * Default number of SelectionKey's that will be handled by the | |
114 * EpollSelector. | |
115 */ | |
116 public const uint DefaultSize = 64; | |
117 /** | |
118 * Default maximum number of events that will be received per | |
119 * invocation to select(). | |
120 */ | |
121 public const uint DefaultMaxEvents = 16; | |
122 | |
123 | |
124 /** Map to associate the conduit handles with their selection keys */ | |
125 private SelectionKey[ISelectable.Handle] _keys; | |
126 /** File descriptor returned by the epoll_create() system call. */ | |
127 private int _epfd = -1; | |
128 /** | |
129 * Array of events that is filled by epoll_wait() inside the call | |
130 * to select(). | |
131 */ | |
132 private epoll_event[] _events; | |
133 /** Number of events resulting from the call to select() */ | |
134 private int _eventCount = 0; | |
135 | |
136 | |
137 /** | |
138 * Destructor | |
139 */ | |
140 ~this() | |
141 { | |
142 // Make sure that we release the epoll file descriptor once this | |
143 // object is garbage collected. | |
144 close(); | |
145 } | |
146 | |
147 /** | |
148 * Open the epoll selector, makes a call to epoll_create() | |
149 * | |
150 * Params: | |
151 * size = maximum amount of conduits that will be registered; | |
152 * it will grow dynamically if needed. | |
153 * maxEvents = maximum amount of conduit events that will be | |
154 * returned in the selection set per call to select(); | |
155 * this limit is enforced by this selector. | |
156 * | |
157 * Throws: | |
158 * SelectorException if there are not enough resources to open the | |
159 * selector (e.g. not enough file handles or memory available). | |
160 */ | |
161 public void open(uint size = DefaultSize, uint maxEvents = DefaultMaxEvents) | |
162 in | |
163 { | |
164 assert(size > 0); | |
165 assert(maxEvents > 0); | |
166 } | |
167 body | |
168 { | |
169 _events = new epoll_event[maxEvents]; | |
170 | |
171 _epfd = epoll_create(cast(int) size); | |
172 if (_epfd < 0) | |
173 { | |
174 checkErrno(__FILE__, __LINE__); | |
175 } | |
176 } | |
177 | |
178 /** | |
179 * Close the selector, releasing the file descriptor that had been | |
180 * created in the previous call to open(). | |
181 * | |
182 * Remarks: | |
183 * It can be called multiple times without harmful side-effects. | |
184 */ | |
185 public void close() | |
186 { | |
187 if (_epfd >= 0) | |
188 { | |
189 .close(_epfd); | |
190 _epfd = -1; | |
191 } | |
192 _events = null; | |
193 _eventCount = 0; | |
194 } | |
195 | |
196 /** | |
197 * Associate a conduit to the selector and track specific I/O events. | |
198 * | |
199 * Params: | |
200 * conduit = conduit that will be associated to the selector; | |
201 * must be a valid conduit (i.e. not null and open). | |
202 * events = bit mask of Event values that represent the events | |
203 * that will be tracked for the conduit. | |
204 * attachment = optional object with application-specific data that | |
205 * will be available when an event is triggered for the | |
206 * conduit | |
207 * | |
208 * Throws: | |
209 * RegisteredConduitException if the conduit had already been | |
210 * registered to the selector; SelectorException if there are not | |
211 * enough resources to add the conduit to the selector. | |
212 * | |
213 * Examples: | |
214 * --- | |
215 * selector.register(conduit, Event.Read | Event.Write, object); | |
216 * --- | |
217 */ | |
218 public void register(ISelectable conduit, Event events, Object attachment = null) | |
219 in | |
220 { | |
221 assert(conduit !is null && conduit.fileHandle() >= 0); | |
222 } | |
223 body | |
224 { | |
225 epoll_event event; | |
226 SelectionKey key = new SelectionKey(conduit, events, attachment); | |
227 | |
228 event.events = events; | |
229 // We associate the selection key to the epoll_event to be able to | |
230 // retrieve it efficiently when we get events for this handle. | |
231 event.data.ptr = cast(void*) key; | |
232 | |
233 if (epoll_ctl(_epfd, EPOLL_CTL_ADD, conduit.fileHandle(), &event) == 0) | |
234 { | |
235 // We keep the keys in a map to make sure that the key is not | |
236 // garbage collected while there is still a reference to it in | |
237 // an epoll_event. This also allows to to efficiently find the | |
238 // key corresponding to a handle in methods where this | |
239 // association is not provided automatically. | |
240 _keys[conduit.fileHandle()] = key; | |
241 } | |
242 else | |
243 { | |
244 checkErrno(__FILE__, __LINE__); | |
245 } | |
246 } | |
247 | |
248 /** | |
249 * Modify the events that are being tracked or the 'attachment' field | |
250 * for an already registered conduit. | |
251 * | |
252 * Params: | |
253 * conduit = conduit that will be associated to the selector; | |
254 * must be a valid conduit (i.e. not null and open). | |
255 * events = bit mask of Event values that represent the events | |
256 * that will be tracked for the conduit. | |
257 * attachment = optional object with application-specific data that | |
258 * will be available when an event is triggered for the | |
259 * conduit | |
260 * | |
261 * Remarks: | |
262 * The 'attachment' member of the SelectionKey will always be | |
263 * overwritten, even if it's null. | |
264 * | |
265 * Throws: | |
266 * UnregisteredConduitException if the conduit had not been previously | |
267 * registered to the selector; SelectorException if there are not | |
268 * enough resources to modify the conduit registration. | |
269 * | |
270 * Examples: | |
271 * --- | |
272 * selector.reregister(conduit, Event.Write, object); | |
273 * --- | |
274 */ | |
275 public void reregister(ISelectable conduit, Event events, Object attachment = null) | |
276 in | |
277 { | |
278 assert(conduit !is null && conduit.fileHandle() >= 0); | |
279 } | |
280 body | |
281 { | |
282 SelectionKey* key = (conduit.fileHandle() in _keys); | |
283 | |
284 if (key !is null) | |
285 { | |
286 epoll_event event; | |
287 | |
288 (*key).events = events; | |
289 (*key).attachment = attachment; | |
290 | |
291 event.events = events; | |
292 event.data.ptr = cast(void*) *key; | |
293 | |
294 if (epoll_ctl(_epfd, EPOLL_CTL_MOD, conduit.fileHandle(), &event) != 0) | |
295 { | |
296 checkErrno(__FILE__, __LINE__); | |
297 } | |
298 } | |
299 else | |
300 { | |
301 throw new UnregisteredConduitException(__FILE__, __LINE__); | |
302 } | |
303 } | |
304 | |
305 /** | |
306 * Remove a conduit from the selector. | |
307 * | |
308 * Params: | |
309 * conduit = conduit that had been previously associated to the | |
310 * selector; it can be null. | |
311 * | |
312 * Remarks: | |
313 * Unregistering a null conduit is allowed and no exception is thrown | |
314 * if this happens. | |
315 * | |
316 * Throws: | |
317 * UnregisteredConduitException if the conduit had not been previously | |
318 * registered to the selector; SelectorException if there are not | |
319 * enough resources to remove the conduit registration. | |
320 */ | |
321 public void unregister(ISelectable conduit) | |
322 { | |
323 if (conduit !is null) | |
324 { | |
325 if (epoll_ctl(_epfd, EPOLL_CTL_DEL, conduit.fileHandle(), null) == 0) | |
326 { | |
327 _keys.remove(conduit.fileHandle()); | |
328 } | |
329 else | |
330 { | |
331 checkErrno(__FILE__, __LINE__); | |
332 } | |
333 } | |
334 } | |
335 | |
336 /** | |
337 * Wait for I/O events from the registered conduits for a specified | |
338 * amount of time. | |
339 * | |
340 * Params: | |
341 * timeout = TimeSpan with the maximum amount of time that the | |
342 * selector will wait for events from the conduits; the | |
343 * amount of time is relative to the current system time | |
344 * (i.e. just the number of milliseconds that the selector | |
345 * has to wait for the events). | |
346 * | |
347 * Returns: | |
348 * The amount of conduits that have received events; 0 if no conduits | |
349 * have received events within the specified timeout; and -1 if the | |
350 * wakeup() method has been called from another thread. | |
351 * | |
352 * Throws: | |
353 * InterruptedSystemCallException if the underlying system call was | |
354 * interrupted by a signal and the 'restartInterruptedSystemCall' | |
355 * property was set to false; SelectorException if there were no | |
356 * resources available to wait for events from the conduits. | |
357 */ | |
358 public int select(TimeSpan timeout) | |
359 { | |
360 int to = (timeout != TimeSpan.max ? cast(int) timeout.millis : -1); | |
361 | |
362 while (true) | |
363 { | |
364 // FIXME: add support for the wakeup() call. | |
365 _eventCount = epoll_wait(_epfd, _events.ptr, _events.length, to); | |
366 if (_eventCount >= 0) | |
367 { | |
368 break; | |
369 } | |
370 else | |
371 { | |
372 if (errno != EINTR || !_restartInterruptedSystemCall) | |
373 { | |
374 checkErrno(__FILE__, __LINE__); | |
375 } | |
376 debug (selector) | |
377 Stdout("--- Restarting epoll_wait() after being interrupted by a signal\n"); | |
378 } | |
379 } | |
380 return _eventCount; | |
381 } | |
382 | |
383 /** | |
384 * Return the selection set resulting from the call to any of the | |
385 * select() methods. | |
386 * | |
387 * Remarks: | |
388 * If the call to select() was unsuccessful or it did not return any | |
389 * events, the returned value will be null. | |
390 */ | |
391 public ISelectionSet selectedSet() | |
392 { | |
393 return (_eventCount > 0 ? new EpollSelectionSet(_events[0.._eventCount]) : null); | |
394 } | |
395 | |
396 /** | |
397 * Return the selection key resulting from the registration of a | |
398 * conduit to the selector. | |
399 * | |
400 * Remarks: | |
401 * If the conduit is not registered to the selector the returned | |
402 * value will be null. No exception will be thrown by this method. | |
403 */ | |
404 public SelectionKey key(ISelectable conduit) | |
405 { | |
406 return (conduit !is null ? _keys[conduit.fileHandle()] : null); | |
407 } | |
408 | |
409 unittest | |
410 { | |
411 } | |
412 } | |
413 | |
414 /** | |
415 * Class used to hold the list of Conduits that have received events. | |
416 */ | |
417 private class EpollSelectionSet: ISelectionSet | |
418 { | |
419 private epoll_event[] _events; | |
420 | |
421 protected this(epoll_event[] events) | |
422 { | |
423 _events = events; | |
424 } | |
425 | |
426 public uint length() | |
427 { | |
428 return _events.length; | |
429 } | |
430 | |
431 /** | |
432 * Iterate over all the Conduits that have received events. | |
433 */ | |
434 public int opApply(int delegate(inout SelectionKey) dg) | |
435 { | |
436 int rc = 0; | |
437 SelectionKey key; | |
438 | |
439 debug (selector) | |
440 Stdout.format("--- EpollSelectionSet.opApply() ({0} events)\n", _events.length); | |
441 | |
442 foreach (epoll_event event; _events) | |
443 { | |
444 // Only invoke the delegate if there is an event for the conduit. | |
445 if (event.events != 0) | |
446 { | |
447 key = cast(SelectionKey) event.data.ptr; | |
448 key.events = cast(Event) event.events; | |
449 | |
450 debug (selector) | |
451 Stdout.format("--- Event 0x{0:x} for handle {1}\n", | |
452 cast(uint) event.events, cast(int) key.conduit.fileHandle()); | |
453 | |
454 rc = dg(key); | |
455 if (rc != 0) | |
456 { | |
457 break; | |
458 } | |
459 } | |
460 } | |
461 return rc; | |
462 } | |
463 } | |
464 } | |
465 |