1 module concurrency.timer;
2 
3 import concurrency.stoptoken;
4 import core.time : Duration;
5 
6 /// waits for dur and returns true
7 /// or false when stoptoken is triggered
8 bool wait(StopToken stopToken, Duration dur) nothrow @trusted {
9   // this is a optimisation,
10   // it will still work if a stop is triggered before the
11   // onStop handler is set
12   if (stopToken.isStopRequested)
13     return false;
14 
15   try {
16     version (Windows) {
17       import core.sync.mutex : Mutex;
18       import core.sync.condition : Condition;
19 
20       auto m = new Mutex();
21       auto cond = new Condition(m);
22       auto cb = stopToken.onStop(cast(void delegate() shared nothrow @safe)() nothrow @trusted {
23           m.lock_nothrow();
24           scope (exit)
25             m.unlock_nothrow();
26           try {
27             cond.notify();
28           }
29           catch (Exception e) {
30             assert(false, e.msg);
31           }
32         });
33       scope (exit)
34         cb.dispose();
35 
36       /// wait returns true if notified, we want to return false in that case as it signifies cancellation
37       m.lock_nothrow();
38       scope(exit) m.unlock_nothrow();
39       return !cond.wait(dur);
40     } else version (Posix) {
41       import core.sys.linux.timerfd;
42       import core.sys.linux.sys.eventfd;
43       import core.sys.posix.sys.select;
44       import std.exception : ErrnoException;
45       import core.sys.posix.unistd;
46       import core.stdc.errno;
47 
48       shared int stopfd = eventfd(0, EFD_CLOEXEC);
49       if (stopfd == -1)
50         throw new ErrnoException("eventfd failed");
51 
52       auto cb = stopToken.onStop(() shared @trusted {
53           ulong b = 1;
54           write(stopfd, &b, typeof(b).sizeof);
55         });
56       scope (exit) {
57         cb.dispose();
58         close(stopfd);
59       }
60 
61       auto when = dur.split!("seconds", "usecs");
62       fd_set read_fds;
63       FD_ZERO(&read_fds);
64       FD_SET(stopfd, &read_fds);
65       timeval tv;
66       tv.tv_sec = when.seconds;
67       tv.tv_usec = when.usecs;
68     retry:
69       const ret = select(stopfd + 1, &read_fds, null, null, &tv);
70       if (ret == 0) {
71         return true;
72       } else if (ret == -1) {
73         if (errno == EINTR || errno == EAGAIN)
74           goto retry;
75         throw new Exception("wtf select");
76       } else {
77         return false;
78       }
79     }
80   } catch (Exception e) {
81     assert(false, e.msg);
82   }
83 }