]> git.tdb.fi Git - libs/core.git/blob - source/time/timer.cpp
Check errors from CreateSemaphore
[libs/core.git] / source / time / timer.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/core/raii.h>
3 #include "timer.h"
4 #include "utils.h"
5
6 using namespace std;
7
8 namespace Msp {
9 namespace Time {
10
11 Timer::Timer():
12         sem(1)
13 { }
14
15 Timer::Slot &Timer::add(const TimeDelta &td)
16 {
17         unique_ptr<Slot> s = make_unique<Slot>(td);
18         Slot &sref = *s;
19         MutexLock l(mutex);
20         slots.push_back({ move(s) });
21         push_heap(slots.begin(), slots.end());
22         if(blocking)
23                 sem.signal();
24         return sref;
25 }
26
27 Timer::Slot &Timer::add(const TimeStamp &ts)
28 {
29         unique_ptr<Slot> s = make_unique<Slot>(ts);
30         Slot &sref = *s;
31         MutexLock l(mutex);
32         slots.push_back({ move(s) });
33         push_heap(slots.begin(), slots.end());
34         if(blocking)
35                 sem.signal();
36         return sref;
37 }
38
39 void Timer::cancel(Slot &slot)
40 {
41         MutexLock l(mutex);
42         auto i = find_if(slots, [&slot](const SlotProxy &p){ return p.slot.get()==&slot; });
43         if(i!=slots.end())
44         {
45                 slots.erase(i);
46                 make_heap(slots.begin(), slots.end());
47         }
48 }
49
50 void Timer::tick()
51 {
52         do_tick(-sec);
53 }
54
55 void Timer::tick(const TimeDelta &timeout)
56 {
57         if(timeout<zero)
58                 throw invalid_argument("Timer::tick");
59
60         do_tick(timeout);
61 }
62
63 void Timer::do_tick(const TimeDelta &timeout)
64 {
65         TimeStamp deadline;
66         if(timeout>=zero)
67                 deadline = now()+timeout;
68
69         unique_ptr<Slot> next;
70         {
71                 MutexLock l(mutex);
72                 while(1)
73                 {
74                         TimeStamp stamp;
75                         TimeStamp t = now();
76                         if(!slots.empty())
77                         {
78                                 stamp = slots.front().slot->get_timeout();
79                                 if(stamp<=t)
80                                         break;
81                         }
82
83                         if(timeout && (!deadline || t<deadline))
84                         {
85                                 SetFlag setf(blocking);
86                                 mutex.unlock();
87                                 if(stamp && (!deadline || stamp<deadline))
88                                         sem.wait(stamp-t);
89                                 else if(deadline)
90                                         sem.wait(deadline-t);
91                                 else
92                                         sem.wait();
93                                 mutex.lock();
94                                 // The slots may have changed while waiting so check again
95                                 continue;
96                         }
97                         else
98                                 return;
99                 }
100
101                 next = move(slots.front().slot);
102                 pop_heap(slots.begin(), slots.end());
103                 slots.pop_back();
104         }
105
106         if(next->signal_timeout.emit() && next->increment())
107         {
108                 MutexLock l(mutex);
109                 slots.push_back({ move(next) });
110                 push_heap(slots.begin(), slots.end());
111         }
112 }
113
114 TimeStamp Timer::get_next_timeout() const
115 {
116         if(slots.empty())
117                 return TimeStamp();
118         return slots.begin()->slot->get_timeout();
119 }
120
121
122 Timer::Slot::Slot(const TimeDelta &td):
123         interval(td),
124         timeout(now()+interval)
125 { }
126
127 Timer::Slot::Slot(const TimeStamp &ts):
128         timeout(ts)
129 { }
130
131 bool Timer::Slot::increment()
132 {
133         if(!interval)
134                 return false;
135         timeout += interval;
136         return true;
137 }
138
139
140 bool Timer::SlotProxy::operator<(const SlotProxy &sp) const
141 {
142         return slot->get_timeout()>sp.slot->get_timeout();
143 }
144
145 } // namespace Time
146 } // namespace Msp