]> git.tdb.fi Git - libs/core.git/blob - source/time/timer.cpp
Add move semantics to Variant
[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::~Timer()
16 {
17         for(const SlotProxy &s: slots)
18                 delete s.slot;
19 }
20
21 Timer::Slot &Timer::add(const TimeDelta &td)
22 {
23         Slot *s = new Slot(td);
24         MutexLock l(mutex);
25         slots.push_back({ s });
26         push_heap(slots.begin(), slots.end());
27         if(blocking)
28                 sem.signal();
29         return *s;
30 }
31
32 Timer::Slot &Timer::add(const TimeStamp &ts)
33 {
34         Slot *s = new Slot(ts);
35         MutexLock l(mutex);
36         slots.push_back({ s });
37         push_heap(slots.begin(), slots.end());
38         if(blocking)
39                 sem.signal();
40         return *s;
41 }
42
43 void Timer::cancel(Slot &slot)
44 {
45         MutexLock l(mutex);
46         auto i = find_member(slots, &slot, &SlotProxy::slot);
47         if(i!=slots.end())
48         {
49                 delete i->slot;
50                 slots.erase(i);
51                 make_heap(slots.begin(), slots.end());
52         }
53 }
54
55 void Timer::tick()
56 {
57         do_tick(-sec);
58 }
59
60 void Timer::tick(const TimeDelta &timeout)
61 {
62         if(timeout<zero)
63                 throw invalid_argument("Timer::tick");
64
65         do_tick(timeout);
66 }
67
68 void Timer::do_tick(const TimeDelta &timeout)
69 {
70         TimeStamp deadline;
71         if(timeout>=zero)
72                 deadline = now()+timeout;
73
74         Slot *next = nullptr;
75         {
76                 MutexLock l(mutex);
77                 while(1)
78                 {
79                         TimeStamp stamp;
80                         TimeStamp t = now();
81                         if(!slots.empty())
82                         {
83                                 next = slots.begin()->slot;
84                                 stamp = next->get_timeout();
85                                 if(stamp<=t)
86                                         break;
87                         }
88
89                         if(timeout && (!deadline || t<deadline))
90                         {
91                                 SetFlag setf(blocking);
92                                 mutex.unlock();
93                                 if(stamp && (!deadline || stamp<deadline))
94                                         sem.wait(stamp-t);
95                                 else if(deadline)
96                                         sem.wait(deadline-t);
97                                 else
98                                         sem.wait();
99                                 mutex.lock();
100                                 // The slots may have changed while waiting so check again
101                                 continue;
102                         }
103                         else
104                                 return;
105                 }
106
107                 pop_heap(slots.begin(), slots.end());
108                 slots.pop_back();
109         }
110
111         try
112         {
113                 if(next->signal_timeout.emit() && next->increment())
114                 {
115                         MutexLock l(mutex);
116                         slots.push_back({ next });
117                         push_heap(slots.begin(), slots.end());
118                 }
119                 else
120                         delete next;
121         }
122         catch(...)
123         {
124                 delete next;
125                 throw;
126         }
127 }
128
129 TimeStamp Timer::get_next_timeout() const
130 {
131         if(slots.empty())
132                 return TimeStamp();
133         return slots.begin()->slot->get_timeout();
134 }
135
136
137 Timer::Slot::Slot(const TimeDelta &td):
138         interval(td),
139         timeout(now()+interval)
140 { }
141
142 Timer::Slot::Slot(const TimeStamp &ts):
143         timeout(ts)
144 { }
145
146 bool Timer::Slot::increment()
147 {
148         if(!interval)
149                 return false;
150         timeout += interval;
151         return true;
152 }
153
154
155 bool Timer::SlotProxy::operator<(const SlotProxy &sp) const
156 {
157         return slot->get_timeout()>sp.slot->get_timeout();
158 }
159
160 } // namespace Time
161 } // namespace Msp