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