1 /// Provides implementation of asynchronous timer.
2 module dpromise.utils.timer;
3 
4 import core.time;
5 import dpromise.promise;
6 import deimos.libuv.uv, dpromise.internal.libuv;
7 
8 
9 /++
10 Sleep in asynchronous while $(D dur).
11 
12 If an error occurred, promise will be rejected.
13 
14 Params:
15   dur = Duration of sleep.
16 
17 See_Also: $(HTTP https://dlang.org/phobos/core_thread.html#.Thread.sleep, core.thread.Thread.sleep)
18 +/
19 nothrow Promise!void sleepAsync(Duration dur) {
20   return promise!void((res, rej) {
21     nothrow void f(Exception e) {
22       e is null ? res() : rej(e);
23     }
24 
25     sleepAsyncWithCallback(dur, &f);
26   });
27 }
28 
29 ///
30 @system unittest {
31   import dpromise.utils : runEventloop;
32   import std.datetime : Clock, SysTime, UTC;
33 
34   auto startTime = Clock.currTime(UTC());
35 
36   sleepAsync(100.msecs).then({
37     auto dur = Clock.currTime(UTC()) - startTime;
38     assert(dur + 4.msecs > 100.msecs);
39     assert(dur - 4.msecs < 100.msecs);
40   });
41 
42   runEventloop();
43 }
44 
45 
46 /++
47 Sleep in asynchronous while $(D dur) then calls the $(D callback) function.
48 
49 If an error occurred, the $(D callback) function will be called with the error.
50 
51 Params:
52   dur = Duration of sleep.
53   callback = a function called when operation finished or an error occurred.
54 +/
55 nothrow @safe void sleepAsyncWithCallback(Duration dur, void delegate(Exception) nothrow callback)
56 in {
57   assert(callback !is null);
58 } body {
59   struct Data {
60     int err;
61     void delegate(Exception) nothrow callback;
62   }
63 
64   extern(C) nothrow @trusted static void ret(uv_timer_t* tm) {
65     auto data = cast(Data*)tm.data;
66     auto callback = data.callback;
67     callback(factory(data.err));
68 
69     scope(exit) {
70       import core.memory : GC;
71       import core.stdc.stdlib : free;
72       GC.removeRoot(callback.ptr);
73       free(tm.data);
74       free(tm);
75     }
76   }
77 
78   extern(C) nothrow @trusted static void onTimeout(uv_timer_t* tm) {
79     ret(tm);
80   }
81 
82   () @trusted nothrow {
83     import core.time : Duration, TickDuration, to;
84     auto timeout = to!("msecs", ulong)(cast(TickDuration)dur);
85 
86     import core.memory : GC;
87     auto timer = castMalloc!uv_timer_t;
88     uv_timer_init(localLoop, timer);
89 
90     auto data = castMalloc!Data;
91     GC.addRoot(callback.ptr);
92     data.callback = callback;
93     timer.data = data;
94 
95     data.err = uv_timer_start(timer, &onTimeout, timeout, 0);
96     if(data.err != 0) ret(timer);
97   }();
98 }
99 ///
100 @safe unittest {
101   import dpromise.utils : runEventloop;
102   import std.datetime : Clock, UTC;
103 
104   auto startTime = Clock.currTime(UTC());
105   sleepAsyncWithCallback(100.msecs, (e) nothrow {
106     try {
107       auto dur = Clock.currTime(UTC()) - startTime;
108       assert(dur + 4.msecs > 100.msecs);
109       assert(dur - 4.msecs < 100.msecs);
110     } catch(Exception e) {}
111   });
112 
113   runEventloop();
114 }