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 }