236 lines
7.1 KiB
C++
236 lines
7.1 KiB
C++
// 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_
|
|
|