1 /**
2 	A `select` based event driver implementation.
3 
4 	This driver works on all BSD socket compatible operating systems, including
5 	Windows. It has a good performance for small numbers of cuncurrently open
6 	files/sockets, but is not suited for larger amounts.
7 */
8 module eventcore.drivers.posix.select;
9 @safe: /*@nogc:*/ nothrow:
10 
11 public import eventcore.drivers.posix.driver;
12 import eventcore.internal.utils;
13 
14 import core.time : Duration;
15 
16 version (Posix) {
17 	import core.sys.posix.sys.time : timeval;
18 	import core.sys.posix.sys.select;
19 }
20 
21 version (Windows) {
22 	import core.sys.windows.winsock2;
23 }
24 
25 
26 alias SelectEventDriver = PosixEventDriver!SelectEventLoop;
27 
28 final class SelectEventLoop : PosixEventLoop {
29 @safe: nothrow:
30 	override bool doProcessEvents(Duration timeout)
31 	{
32 		//assert(Fiber.getThis() is null, "processEvents may not be called from within a fiber!");
33 //scope (failure) assert(false); import std.stdio; writefln("%.3f: process %s ms", Clock.currAppTick.usecs * 1e-3, timeout.total!"msecs");
34 //scope (success) writefln("%.3f: process out", Clock.currAppTick.usecs * 1e-3);
35 
36 		auto ts = timeout.toTimeVal;
37 
38 		fd_set readfds, writefds, statusfds;
39 
40 		() @trusted {
41 			FD_ZERO(&readfds);
42 			FD_ZERO(&writefds);
43 			FD_ZERO(&statusfds);
44 		} ();
45 		enumerateFDs!(EventType.read)((fd) @trusted { FD_SET(cast(sock_t)fd, &readfds); });
46 		enumerateFDs!(EventType.write)((fd) @trusted { FD_SET(cast(sock_t)fd, &writefds); });
47 		enumerateFDs!(EventType.status)((fd) @trusted { FD_SET(cast(sock_t)fd, &statusfds); });
48 
49 //print("Wait for event... %s", timeout);
50 //writefln("%.3f: select in", Clock.currAppTick.usecs * 1e-3);
51 		auto ret = () @trusted { return select(this.maxFD+1, &readfds, &writefds, &statusfds, timeout == Duration.max ? null : &ts); } ();
52 //writefln("%.3f: select out", Clock.currAppTick.usecs * 1e-3);
53 //print("Done wait for event...");
54 		if (ret > 0) {
55 			enumerateFDs!(EventType.read)((fd) @trusted {
56 				if (FD_ISSET(cast(sock_t)fd, &readfds))
57 					notify!(EventType.read)(fd);
58 			});
59 			enumerateFDs!(EventType.write)((fd) @trusted {
60 				if (FD_ISSET(cast(sock_t)fd, &writefds))
61 					notify!(EventType.write)(fd);
62 			});
63 			enumerateFDs!(EventType.status)((fd) @trusted {
64 				if (FD_ISSET(cast(sock_t)fd, &statusfds))
65 					notify!(EventType.status)(fd);
66 			});
67 			return true;
68 		} else {
69 			// NOTE: In particular, EINTR needs to cause true to be returned
70 			//       here, so that user code has a chance to handle any effects
71 			//       of the signal handler before waiting again.
72 			//
73 			//       Other errors are very likely to to reoccur for the next
74 			//       loop iteration, so there is no value in attempting to
75 			//       wait again.
76 			return ret < 0;
77 		}
78 	}
79 
80 	override void dispose()
81 	{
82 		super.dispose();
83 	}
84 
85 	override void registerFD(FD fd, EventMask mask, bool edge_triggered = true)
86 	{
87 	}
88 
89 	override void unregisterFD(FD fd, EventMask mask)
90 	{
91 	}
92 
93 	override void updateFD(FD fd, EventMask old_mask, EventMask mask, bool edge_triggered = true)
94 	{
95 	}
96 }
97 
98 private timeval toTimeVal(Duration dur)
99 {
100 	timeval tvdur;
101 	dur.split!("seconds", "usecs")(tvdur.tv_sec, tvdur.tv_usec);
102 	return tvdur;
103 }