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