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