]> git.tdb.fi Git - libs/core.git/blob - source/time/timer.cpp
Split Timer::tick into two overloads
[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         do_tick(timeout);
73 }
74
75 void Timer::do_tick(const TimeDelta &timeout)
76 {
77         TimeStamp deadline;
78         if(timeout>=zero)
79                 deadline = now()+timeout;
80
81         Slot *next = 0;
82         {
83                 MutexLock l(mutex);
84                 while(1)
85                 {
86                         TimeStamp stamp;
87                         TimeStamp t = now();
88                         if(!slots.empty())
89                         {
90                                 next = slots.begin()->slot;
91                                 stamp = next->get_timeout();
92                                 if(stamp<=t)
93                                         break;
94                         }
95
96                         if(timeout && (!deadline || t<deadline))
97                         {
98                                 SetFlag setf(blocking);
99                                 mutex.unlock();
100                                 if(stamp && (!deadline || stamp<deadline))
101                                         sem.wait(stamp-t);
102                                 else if(deadline)
103                                         sem.wait(deadline-t);
104                                 else
105                                         sem.wait();
106                                 mutex.lock();
107                                 // The slots may have changed while waiting so check again
108                                 continue;
109                         }
110                         else
111                                 return;
112                 }
113
114                 pop_heap(slots.begin(), slots.end());
115                 slots.pop_back();
116         }
117
118         try
119         {
120                 if(next->signal_timeout.emit() && next->increment())
121                 {
122                         MutexLock l(mutex);
123                         slots.push_back(next);
124                         push_heap(slots.begin(), slots.end());
125                 }
126                 else
127                         delete next;
128         }
129         catch(...)
130         {
131                 delete next;
132                 throw;
133         }
134 }
135
136 TimeStamp Timer::get_next_timeout() const
137 {
138         if(slots.empty())
139                 return TimeStamp();
140         return slots.begin()->slot->get_timeout();
141 }
142
143
144 Timer::Slot::Slot(const TimeDelta &td):
145         interval(td),
146         timeout(now()+interval)
147 { }
148
149 Timer::Slot::Slot(const TimeStamp &ts):
150         timeout(ts)
151 { }
152
153 bool Timer::Slot::increment()
154 {
155         if(!interval)
156                 return false;
157         timeout += interval;
158         return true;
159 }
160
161
162 Timer::SlotProxy::SlotProxy(Slot *s):
163         slot(s)
164 { }
165
166 bool Timer::SlotProxy::operator<(const SlotProxy &sp) const
167 {
168         return slot->get_timeout()>sp.slot->get_timeout();
169 }
170
171 } // namespace Time
172 } // namespace Msp