// 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_