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