1 // Copyright (C) 2005  Davis E. King (davis@dlib.net)
2 // License: Boost Software License   See LICENSE.txt for the full license.
3 #ifndef DLIB_TIMER_KERNEl_1_
4 #define DLIB_TIMER_KERNEl_1_
5 
6 #include "../threads.h"
7 #include "../algs.h"
8 #include "../misc_api.h"
9 #include "timer_abstract.h"
10 
11 namespace dlib
12 {
13 
14     template <
15         typename T
16         >
17     class timer_heavy
18     {
19         /*!
20             WHAT THIS OBJECT REPRESENTS
21                 This is an implementation of the timer_abstract.h interface.  It is very
22                 simple and uses only one thread which is always alive in a timer_heavy.
23                 The reason this object exists is for historical reasons.  Originally, the
24                 dlib::timer was a multi-implementation component and the timer_heavy was
25                 its first implementation.  It was superseded later by the more efficient
26                 dlib::timer.  However, timer_heavy is still around so that
27                 dlib::timer::kernel_1a has something to refer to.  This way, old client
28                 code which somehow depends on the same thread always calling a timer action
29                 function isn't going to be disrupted.
30 
31 
32             INITIAL VALUE
33                 - running   == false
34                 - delay     == 1000
35                 - ao        == a pointer to the action_object()
36                 - af        == a pointer to the action_function()
37                 - m         == a mutex that locks everything in this class
38                 - s         == a signaler for mutex m
39                 - stop_running == false
40 
41             CONVENTION
42                 - running && !stop_running == is_running()
43                 - delay == delay_time()
44                 - *ao == action_object()
45                 - af == action_function()
46 
47                 - if (running) then
48                     - there is a thread running
49                 - if (is_running()) then
50                     - next_time_to_run == the time when the next execution of the action
51                       function should occur.  (the time is given by ts.get_timestamp())
52 
53                 - stop_running is used to tell the thread to quit.  If it is
54                   set to true then the thread should end.
55         !*/
56 
57     public:
58 
59         typedef void (T::*af_type)();
60 
61         timer_heavy(
62             T& ao_,
63             af_type af_
64         );
65 
66         virtual ~timer_heavy(
67         );
68 
69         void clear(
70         );
71 
72         af_type action_function (
73         ) const;
74 
75         const T& action_object (
76         ) const;
77 
78         T& action_object (
79         );
80 
81         bool is_running (
82         ) const;
83 
84         unsigned long delay_time (
85         ) const;
86 
87         void set_delay_time (
88             unsigned long milliseconds
89         );
90 
91         void start (
92         );
93 
94         void stop (
95         );
96 
97         void stop_and_wait (
98         );
99 
100     private:
101 
102         void thread (
103         );
104         /*!
105             requires
106                 - is run in its own thread
107             ensures
108                 - calls the action function for the given timer object in the manner
109                   specified by timer_kernel_abstract.h
110         !*/
111 
112         // data members
113         T& ao;
114         const af_type af;
115         unsigned long delay;
116         mutex m;
117         signaler s;
118 
119         bool running;
120         bool stop_running;
121         timestamper ts;
122         uint64 next_time_to_run;
123 
124         // restricted functions
125         timer_heavy(const timer_heavy<T>&);        // copy constructor
126         timer_heavy<T>& operator=(const timer_heavy<T>&);    // assignment operator
127 
128     };
129 
130 // ----------------------------------------------------------------------------------------
131 // ----------------------------------------------------------------------------------------
132     // member function definitions
133 // ----------------------------------------------------------------------------------------
134 // ----------------------------------------------------------------------------------------
135 
136     template <
137         typename T
138         >
139     timer_heavy<T>::
timer_heavy(T & ao_,af_type af_)140     timer_heavy(
141         T& ao_,
142         af_type af_
143     ) :
144         ao(ao_),
145         af(af_),
146         delay(1000),
147         s(m),
148         running(false),
149         stop_running(false)
150     {
151     }
152 
153 // ----------------------------------------------------------------------------------------
154 
155     template <
156         typename T
157         >
158     timer_heavy<T>::
~timer_heavy()159     ~timer_heavy(
160     )
161     {
162         stop_and_wait();
163     }
164 
165 // ----------------------------------------------------------------------------------------
166 
167     template <
168         typename T
169         >
170     void timer_heavy<T>::
clear()171     clear(
172     )
173     {
174         m.lock();
175         stop_running = true;
176         delay = 1000;
177         s.broadcast();
178         m.unlock();
179     }
180 
181 // ----------------------------------------------------------------------------------------
182 
183     template <
184         typename T
185         >
186     typename timer_heavy<T>::af_type timer_heavy<T>::
action_function()187     action_function (
188     ) const
189     {
190         return af;
191     }
192 
193 // ----------------------------------------------------------------------------------------
194 
195     template <
196         typename T
197         >
198     const T& timer_heavy<T>::
action_object()199     action_object (
200     ) const
201     {
202         return ao;
203     }
204 
205 // ----------------------------------------------------------------------------------------
206 
207     template <
208         typename T
209         >
210     T& timer_heavy<T>::
action_object()211     action_object (
212     )
213     {
214         return ao;
215     }
216 
217 // ----------------------------------------------------------------------------------------
218 
219     template <
220         typename T
221         >
222     bool timer_heavy<T>::
is_running()223     is_running (
224     ) const
225     {
226         auto_mutex M(m);
227         return running && !stop_running;
228     }
229 
230 // ----------------------------------------------------------------------------------------
231 
232     template <
233         typename T
234         >
235     unsigned long timer_heavy<T>::
delay_time()236     delay_time (
237     ) const
238     {
239         auto_mutex M(m);
240         return delay;
241     }
242 
243 // ----------------------------------------------------------------------------------------
244 
245     template <
246         typename T
247         >
248     void timer_heavy<T>::
set_delay_time(unsigned long milliseconds)249     set_delay_time (
250         unsigned long milliseconds
251     )
252     {
253         m.lock();
254 
255         // if (is_running()) then we should adjust next_time_to_run
256         if (running && !stop_running)
257         {
258             next_time_to_run -= delay*1000;
259             next_time_to_run += milliseconds*1000;
260         }
261 
262         delay = milliseconds;
263         s.broadcast();
264         m.unlock();
265     }
266 
267 // ----------------------------------------------------------------------------------------
268 
269     template <
270         typename T
271         >
272     void timer_heavy<T>::
start()273     start (
274     )
275     {
276         auto_mutex M(m);
277 
278         // if (is_running() == false) then reset the countdown to the next call
279         // to the action_function()
280         if ( (running && !stop_running) == false)
281             next_time_to_run = ts.get_timestamp() + delay*1000;
282 
283         stop_running = false;
284         if (running == false)
285         {
286             running = true;
287 
288             // start the thread
289             if (create_new_thread<timer_heavy,&timer_heavy::thread>(*this) == false)
290             {
291                 running = false;
292                 throw dlib::thread_error("error creating new thread in timer_heavy::start");
293             }
294         }
295     }
296 
297 // ----------------------------------------------------------------------------------------
298 
299     template <
300         typename T
301         >
302     void timer_heavy<T>::
stop()303     stop (
304     )
305     {
306         m.lock();
307         stop_running = true;
308         s.broadcast();
309         m.unlock();
310     }
311 
312 // ----------------------------------------------------------------------------------------
313 
314     template <
315         typename T
316         >
317     void timer_heavy<T>::
thread()318     thread (
319     )
320     {
321         auto_mutex M(m);
322         unsigned long delay_remaining;
323         uint64 current_time = ts.get_timestamp();
324 
325         if (current_time < next_time_to_run)
326             delay_remaining = static_cast<unsigned long>((next_time_to_run-current_time)/1000);
327         else
328             delay_remaining = 0;
329 
330         while (stop_running == false)
331         {
332             if (delay_remaining > 0)
333                 s.wait_or_timeout(delay_remaining);
334 
335             if (stop_running)
336                 break;
337 
338             current_time = ts.get_timestamp();
339             if (current_time < next_time_to_run)
340             {
341                 // then we woke up too early so we should keep waiting
342                 delay_remaining = static_cast<unsigned long>((next_time_to_run-current_time)/1000);
343 
344                 // rounding might make this be zero anyway.  So if it is
345                 // then we will say we have hit the next time to run.
346                 if (delay_remaining > 0)
347                     continue;
348             }
349 
350             // call the action function
351             m.unlock();
352             (ao.*af)();
353             m.lock();
354 
355             current_time = ts.get_timestamp();
356             next_time_to_run = current_time + delay*1000;
357             delay_remaining = delay;
358         }
359         running = false;
360         stop_running = false;
361         s.broadcast();
362     }
363 
364 // ----------------------------------------------------------------------------------------
365 
366     template <
367         typename T
368         >
369     void timer_heavy<T>::
stop_and_wait()370     stop_and_wait (
371     )
372     {
373         m.lock();
374         if (running)
375         {
376             // make the running thread terminate
377             stop_running = true;
378 
379             s.broadcast();
380             // wait for the thread to quit
381             while (running)
382                 s.wait();
383         }
384         m.unlock();
385     }
386 
387 // ----------------------------------------------------------------------------------------
388 
389 }
390 
391 #endif // DLIB_TIMER_KERNEl_1_
392 
393