1 /+
2 Threaded sleep
3 +/
4 module sily.async.timer;
5 
6 import std.concurrency;
7 import core.thread;
8 
9 /**
10 Executes delegate on timeout or interval
11 
12 Params:
13   func = Delegate function to execute
14   timespan = Time in milliseconds
15 
16 Example:
17 ---
18 // Using normal functions
19 void func() { writeln("Timer end"); }
20 import std.functional: toDelegate;
21 setTimeout(toDelegate(&func), 6000);
22 
23 // Using inline delegates
24 setTimeout(delegate void() { writeln("Timer end"); }, 6000);
25 
26 // Interval
27 setInterval(delegate void() { writeln("Timer tick"); }, 10);
28 
29 // Stop timer
30 // After stopping timer it cannot be restarted
31 AsyncTimer timer = setInterval(delegate void() { writeln("Timer tick"); }, 10);
32 timer.stop();
33 
34 // Adjusting timer values
35 // Will be adjusted on next cycle (aka after 10 msec)
36 timer = setInterval(delegate void() { writeln("Timer tick"); }, 10);
37 timer.timespan = 50;
38 timer.interval = false; // will prevent timer from running next cycle
39 ---
40 */
41 AsyncTimer setTimeout(void delegate() func, int timespan) {
42     return setAsyncTimer(func, AsyncTimer(timespan, false, new AsyncTimerValues()));
43 }
44 
45 /// Ditto
46 AsyncTimer setInterval(void delegate() func, int timespan) {
47     return setAsyncTimer(func, AsyncTimer(timespan, true, new AsyncTimerValues()));
48 }
49 
50 private AsyncTimer setAsyncTimer(void delegate() func, AsyncTimer timer) {
51     timer.start();
52     spawn(
53         cast(shared) (&timeoutCallback), 
54         cast(shared) func, 
55         cast(shared) (timer.intervalptr()),
56         cast(shared) (timer.timespanptr()),
57         cast(shared) (timer.enabledptr())
58     );
59     return timer;
60 }
61 
62 private static void timeoutCallback(shared void delegate() func, 
63                                     shared bool* interval, 
64                                     shared int* timespan, 
65                                     shared bool* enabled) {
66     do {
67         Thread.sleep((*timespan).msecs);
68         if (*enabled) func();
69     } while ((*enabled) && (*interval));
70     (*(cast(bool*) enabled)) = false; 
71 }
72 
73 private static struct AsyncTimerValues {
74     private bool _enabled = false;
75 
76     private int _timespan = 0;
77 
78     private bool _isInterval = false;
79 }
80 
81 /// Timer struct used by setTimeout and setInverval. Does not contains timer functionality itself.
82 static struct AsyncTimer {
83     private AsyncTimerValues* _timer;
84 
85     @property int  timespan() { return (*_timer)._timespan; }
86     @property void timespan(int p_timespan) { (*_timer)._timespan = p_timespan; }
87 
88     @property bool interval() { return (*_timer)._isInterval; }
89     @property void interval(bool p_isInterval) { (*_timer)._isInterval = p_isInterval; }
90 
91     @property bool enabled() { return (*_timer)._enabled; }
92 
93     @property private bool* enabledptr() { return &((*_timer)._enabled); }
94     @property private int* timespanptr() { return &((*_timer)._timespan); }
95     @property private bool* intervalptr() { return &((*_timer)._isInterval); }
96 
97     private this(int p_timespan, bool p_interval, AsyncTimerValues* timer) {
98         _timer = timer;
99         timespan = p_timespan;
100         interval = p_interval;
101     }
102     
103     /// Stops timer (timeout and interval execution)
104     void stop() {
105         (*_timer)._enabled = false;
106     }
107 
108     /// Will do nothing, used by timeout and interval
109     void start() {
110         (*_timer)._enabled = true;
111     }
112     
113     /// Will sleep until timer is finished (not recommended with interval)
114     void await() {
115         while ((*_timer)._enabled) {
116             Thread.sleep(500.usecs);
117         }
118     }
119 }
120 
121