1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
// Copyright (C) 2007 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_TIMER_cPPh_
#define DLIB_TIMER_cPPh_
#include "timer.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
timer_global_clock::
timer_global_clock(
):
s(m),
shutdown(false),
running(false)
{
}
// ----------------------------------------------------------------------------------------
timer_global_clock::
~timer_global_clock()
{
// The only time this destructor is called is when
//
// a) the process terminates
// b) the dynamic library(.so/.dll) is unloaded (could be a part of a))
//
// in case of a)
// windows: the process termination is especially painful, since threads are killed
// before destructors of the process image .dll's are called.
// Thus, for the windows platform, there is no threads running, so the only thing
// to do here is just let the standard memberwise destructors run
// linux: it's ok to just signal shutdown and wait for the running thread, to exit
//
// in case of b)
// windows:
// if it's part of the termination process, a) applies
// if its part of user doing manual load_library/unload_library
// there is no (safe/robust)solution, but best practices are described here
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn633971.aspx
// to support such a clean shutdown, you are required to make a call prior to
// unload dll, that shutdown all the threads in the contained dll.
// This could be done in this module by providing a global_delete_clock()
//
// linux: the destructor for linux will do it's usual job regardless.
//
#ifndef _WIN32
m.lock();
shutdown = true;
s.signal();
m.unlock();
wait();
#endif
}
// ----------------------------------------------------------------------------------------
void timer_global_clock::
add (
timer_base* r
)
{
if (r->in_global_clock == false)
{
// if the thread isn't running then start it up
if (!running)
{
start();
running = true;
}
uint64 t = ts.get_timestamp() + r->delay*1000;
tm.reset();
if (!tm.move_next() || t < tm.element().key())
{
// we need to make the thread adjust its next time to
// trigger if this new event occurrs sooner than the
// next event in tm
s.signal();
}
timer_base* rtemp = r;
uint64 ttemp = t;
tm.add(ttemp,rtemp);
r->next_time_to_run = t;
r->in_global_clock = true;
}
}
// ----------------------------------------------------------------------------------------
void timer_global_clock::
remove (
timer_base* r
)
{
if (r->in_global_clock)
{
tm.position_enumerator(r->next_time_to_run-1);
do
{
if (tm.element().value() == r)
{
uint64 t;
timer_base* rtemp;
tm.remove_current_element(t,rtemp);
r->in_global_clock = false;
break;
}
} while (tm.move_next());
}
}
// ----------------------------------------------------------------------------------------
void timer_global_clock::
adjust_delay (
timer_base* r,
unsigned long new_delay
)
{
if (r->in_global_clock)
{
remove(r);
// compute the new next_time_to_run and store it in t
uint64 t = r->next_time_to_run;
t -= r->delay*1000;
t += new_delay*1000;
tm.reset();
if (!tm.move_next() || t < tm.element().key())
{
// we need to make the thread adjust its next time to
// trigger if this new event occurrs sooner than the
// next event in tm
s.signal();
}
// set this incase add throws
r->running = false;
r->delay = new_delay;
timer_base* rtemp = r;
uint64 ttemp = t;
tm.add(ttemp,rtemp);
r->next_time_to_run = t;
r->in_global_clock = true;
// put this back now that we know add didn't throw
r->running = true;
}
else
{
r->delay = new_delay;
}
}
// ----------------------------------------------------------------------------------------
void timer_global_clock::
thread()
{
auto_mutex M(m);
while (!shutdown)
{
unsigned long delay = 100000;
tm.reset();
tm.move_next();
// loop and start all the action functions for timers that should have
// triggered.
while(tm.current_element_valid())
{
const uint64 cur_time = ts.get_timestamp();
uint64 t = tm.element().key();
// if the next event in tm is ready to trigger
if (t <= cur_time + 999)
{
// remove this event from the tm map
timer_base* r = tm.element().value();
timer_base* rtemp;
tm.remove_current_element(t,rtemp);
r->in_global_clock = false;
// if this timer is still "running" then start its action function
if (r->running)
{
r->restart();
}
}
else
{
// there aren't any more timers that should trigger so we compute
// the delay to the next timer event.
delay = static_cast<unsigned long>((t - cur_time)/1000);
break;
}
}
s.wait_or_timeout(delay);
}
}
// ----------------------------------------------------------------------------------------
std::shared_ptr<timer_global_clock> get_global_clock()
{
static std::shared_ptr<timer_global_clock> d(new timer_global_clock);
return d;
}
// ----------------------------------------------------------------------------------------
// do this just to make sure get_global_clock() gets called at program startup
class timer_global_clock_helper
{
public:
timer_global_clock_helper()
{
get_global_clock();
}
};
static timer_global_clock_helper call_get_global_clock;
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_TIMER_cPPh_
|