]> git.tdb.fi Git - libs/core.git/blob - source/time/timer.cpp
Refactor 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         Slot *next = 0;
60         {
61                 MutexLock l(mutex);
62                 while(1)
63                 {
64                         TimeStamp stamp;
65                         TimeStamp t = now();
66                         if(!slots.empty())
67                         {
68                                 next = slots.begin()->slot;
69                                 stamp = next->get_timeout();
70                                 if(stamp<=t)
71                                         break;
72                         }
73
74                         if(block)
75                         {
76                                 SetFlag setf(blocking);
77                                 mutex.unlock();
78                                 if(stamp)
79                                         sem.wait(stamp-t);
80                                 else
81                                         sem.wait();
82                                 mutex.lock();
83                                 // The slots may have changed while waiting so check again
84                                 continue;
85                         }
86                         else
87                                 return;
88                 }
89
90                 pop_heap(slots.begin(), slots.end());
91                 slots.pop_back();
92         }
93
94         try
95         {
96                 if(next->signal_timeout.emit() && next->increment())
97                 {
98                         MutexLock l(mutex);
99                         slots.push_back(next);
100                         push_heap(slots.begin(), slots.end());
101                 }
102                 else
103                         delete next;
104         }
105         catch(...)
106         {
107                 delete next;
108                 throw;
109         }
110 }
111
112 TimeStamp Timer::get_next_timeout() const
113 {
114         if(slots.empty())
115                 return TimeStamp();
116         return slots.begin()->slot->get_timeout();
117 }
118
119
120 Timer::Slot::Slot(const TimeDelta &td):
121         interval(td),
122         timeout(now()+interval)
123 { }
124
125 Timer::Slot::Slot(const TimeStamp &ts):
126         timeout(ts)
127 { }
128
129 bool Timer::Slot::increment()
130 {
131         if(!interval)
132                 return false;
133         timeout += interval;
134         return true;
135 }
136
137
138 Timer::SlotProxy::SlotProxy(Slot *s):
139         slot(s)
140 { }
141
142 bool Timer::SlotProxy::operator<(const SlotProxy &sp) const
143 {
144         return slot->get_timeout()>sp.slot->get_timeout();
145 }
146
147 } // namespace Time
148 } // namespace Msp