diff options
Diffstat (limited to 'src/common/timer.cpp')
-rw-r--r-- | src/common/timer.cpp | 334 |
1 files changed, 135 insertions, 199 deletions
diff --git a/src/common/timer.cpp b/src/common/timer.cpp index 66aaa9b..7b115d9 100644 --- a/src/common/timer.cpp +++ b/src/common/timer.cpp @@ -1,257 +1,193 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> +#include "timer.hpp" -#include <sys/socket.h> +#include <sys/stat.h> #include <sys/time.h> -#include "timer.hpp" +#include <cassert> +#include <cstring> + +#include <queue> + +#include "cxxstdio.hpp" #include "utils.hpp" -static struct TimerData *timer_data; -static uint32_t timer_data_max, timer_data_num; -static timer_id *free_timer_list; -static uint32_t free_timer_list_max, free_timer_list_pos; +#include "../poison.hpp" + +struct TimerData +{ + /// This will be reset on call, to avoid problems. + Timer *owner; + + /// When it will be triggered + tick_t tick; + /// What will be done + timer_func func; + /// Repeat rate - 0 for oneshot + interval_t interval; + + TimerData(Timer *o, tick_t t, timer_func f, interval_t i) + : owner(o) + , tick(t) + , func(std::move(f)) + , interval(i) + {} +}; + +struct TimerCompare +{ + /// implement "less than" + bool operator() (TimerData *l, TimerData *r) + { + // C++ provides a max-heap, but we want + // the smallest tick to be the head (a min-heap). + return l->tick > r->tick; + } +}; -/// Okay, I think I understand this structure now: -/// the timer heap is a magic queue that allows inserting timers and then popping them in order -/// designed to copy only log2(N) entries instead of N -// timer_heap[0] is the size (greatest index into the heap) -// timer_heap[1] is the first actual element -// timer_heap_max increases 256 at a time and never decreases -static uint32_t timer_heap_max = 0; -/// FIXME: refactor the code to put the size in a separate variable -//nontrivial because indices get multiplied -static timer_id *timer_heap = NULL; +static +std::priority_queue<TimerData *, std::vector<TimerData *>, TimerCompare> timer_heap; -static uint32_t gettick_cache; -static uint8_t gettick_count = 0; +tick_t gettick_cache; -uint32_t gettick_nocache (void) +tick_t milli_clock::now(void) noexcept { struct timeval tval; // BUG: This will cause strange behavior if the system clock is changed! // it should be reimplemented in terms of clock_gettime(CLOCK_MONOTONIC, ) - gettimeofday (&tval, NULL); - gettick_count = 255; - return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000; + gettimeofday(&tval, NULL); + return gettick_cache = tick_t(std::chrono::seconds(tval.tv_sec) + + std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::microseconds(tval.tv_usec))); } -uint32_t gettick (void) +static +void do_nothing(TimerData *, tick_t) { - if (gettick_count--) - return gettick_cache; - return gettick_nocache (); } -static void push_timer_heap (timer_id index) +void Timer::cancel() { - if (timer_heap == NULL || timer_heap[0] + 1 >= timer_heap_max) - { - timer_heap_max += 256; - RECREATE (timer_heap, timer_id, timer_heap_max); - memset (timer_heap + (timer_heap_max - 256), 0, sizeof (timer_id) * 256); - } -// timer_heap[0] is the greatest index into the heap, which increases - timer_heap[0]++; - - timer_id h = timer_heap[0]-1, i = (h - 1) / 2; - while (h) - { - // avoid wraparound problems, it really means this: - // timer_data[index].tick >= timer_data[timer_heap[i+1]].tick - if ( DIFF_TICK(timer_data[index].tick, timer_data[timer_heap[i+1]].tick) >= 0) - break; - timer_heap[h + 1] = timer_heap[i + 1]; - h = i; - i = (h - 1) / 2; - } - timer_heap[h + 1] = index; + if (!td) + return; + + assert (this == td->owner); + td->owner = nullptr; + td->func = do_nothing; + td->interval = interval_t::zero(); + td = nullptr; } -static timer_id top_timer_heap (void) +void Timer::detach() { - if (!timer_heap || !timer_heap[0]) - return -1; - return timer_heap[1]; + assert (this == td->owner); + td->owner = nullptr; + td = nullptr; } -static timer_id pop_timer_heap (void) +static +void push_timer_heap(TimerData *td) { - if (!timer_heap || !timer_heap[0]) - return -1; - timer_id ret = timer_heap[1]; - timer_id last = timer_heap[timer_heap[0]]; - timer_heap[0]--; - - uint32_t h, k; - for (h = 0, k = 2; k < timer_heap[0]; k = k * 2 + 2) - { - if (DIFF_TICK(timer_data[timer_heap[k + 1]].tick, timer_data[timer_heap[k]].tick) > 0) - k--; - timer_heap[h + 1] = timer_heap[k + 1], h = k; - } - if (k == timer_heap[0]) - timer_heap[h + 1] = timer_heap[k], h = k - 1; - - uint32_t i = (h - 1) / 2; - while (h) - { - if (DIFF_TICK (timer_data[timer_heap[i + 1]].tick, timer_data[last].tick) <= 0) - break; - timer_heap[h + 1] = timer_heap[i + 1]; - h = i; - i = (h - 1) / 2; - } - timer_heap[h + 1] = last; + timer_heap.push(td); +} - return ret; +static +TimerData *top_timer_heap(void) +{ + if (timer_heap.empty()) + return nullptr; + return timer_heap.top(); } -timer_id add_timer (tick_t tick, timer_func func, custom_id_t id, custom_data_t data) +static +void pop_timer_heap(void) { - timer_id i; + timer_heap.pop(); +} - if (free_timer_list_pos) - { - // Retrieve a freed timer id instead of a new one - // I think it should be possible to avoid the loop somehow - do - { - i = free_timer_list[--free_timer_list_pos]; - } - while (i >= timer_data_num && free_timer_list_pos > 0); - } - else - i = timer_data_num; +Timer::Timer(tick_t tick, timer_func func, interval_t interval) +: td(new TimerData(this, tick, std::move(func), interval)) +{ + assert (interval >= interval_t::zero()); - // I have no idea what this is doing - if (i >= timer_data_num) - for (i = timer_data_num; i < timer_data_max && timer_data[i].type; i++) - ; - if (i >= timer_data_num && i >= timer_data_max) - { - if (timer_data_max == 0) - { - timer_data_max = 256; - CREATE (timer_data, struct TimerData, timer_data_max); - } - else - { - timer_data_max += 256; - RECREATE (timer_data, struct TimerData, timer_data_max); - memset (timer_data + (timer_data_max - 256), 0, - sizeof (struct TimerData) * 256); - } - } - timer_data[i].tick = tick; - timer_data[i].func = func; - timer_data[i].id = id; - timer_data[i].data = data; - timer_data[i].type = TIMER_ONCE_AUTODEL; - timer_data[i].interval = 1000; - push_timer_heap (i); - if (i >= timer_data_num) - timer_data_num = i + 1; - return i; + push_timer_heap(td); } -timer_id add_timer_interval (tick_t tick, timer_func func, custom_id_t id, - custom_data_t data, interval_t interval) +Timer::Timer(Timer&& t) +: td(t.td) { - timer_id tid = add_timer (tick, func, id, data); - timer_data[tid].type = TIMER_INTERVAL; - timer_data[tid].interval = interval; - return tid; + t.td = nullptr; + if (td) + { + assert (td->owner == &t); + td->owner = this; + } } -void delete_timer (timer_id id, timer_func func) +Timer& Timer::operator = (Timer&& t) { - if (id == 0 || id >= timer_data_num) + std::swap(td, t.td); + if (td) { - fprintf (stderr, "delete_timer error : no such timer %d\n", id); - abort (); + assert (td->owner == &t); + td->owner = this; } - if (timer_data[id].func != func) + if (t.td) { - fprintf (stderr, "Timer mismatch\n"); - abort (); + assert (t.td->owner == this); + t.td->owner = &t; } - // "to let them disappear" - is this just in case? - timer_data[id].func = NULL; - timer_data[id].type = TIMER_ONCE_AUTODEL; - timer_data[id].tick -= 60 * 60 * 1000; -} - -tick_t addtick_timer (timer_id tid, interval_t tick) -{ - return timer_data[tid].tick += tick; + return *this; } -struct TimerData *get_timer (timer_id tid) +interval_t do_timer(tick_t tick) { - return &timer_data[tid]; -} - -interval_t do_timer (tick_t tick) -{ - timer_id i; /// Number of milliseconds until it calls this again // this says to wait 1 sec if all timers get popped - interval_t nextmin = 1000; + interval_t nextmin = std::chrono::seconds(1); - while ((i = top_timer_heap ()) != (timer_id)-1) + while (TimerData *td = top_timer_heap()) { // while the heap is not empty and - if (DIFF_TICK (timer_data[i].tick, tick) > 0) + if (td->tick > tick) { /// Return the time until the next timer needs to goes off - nextmin = DIFF_TICK (timer_data[i].tick, tick); + nextmin = td->tick - tick; break; } - pop_timer_heap (); - if (timer_data[i].func) - { - if (DIFF_TICK (timer_data[i].tick, tick) < -1000) - { - // If we are too far past the requested tick, call with the current tick instead to fix reregistering problems - timer_data[i].func (i, tick, timer_data[i].id, timer_data[i].data); - } - else - { - timer_data[i].func (i, timer_data[i].tick, timer_data[i].id, timer_data[i].data); - } - } - switch (timer_data[i].type) + pop_timer_heap(); + + // Prevent destroying the object we're in. + // Note: this would be surprising in an interval timer, + // but all interval timers do an immediate explicit detach(). + if (td->owner) + td->owner->detach(); + // If we are too far past the requested tick, call with + // the current tick instead to fix reregistration problems + if (td->tick + std::chrono::seconds(1) < tick) + td->func(td, tick); + else + td->func(td, td->tick); + + if (td->interval == interval_t::zero()) { - case TIMER_ONCE_AUTODEL: - timer_data[i].type = TIMER_NONE; - if (free_timer_list_pos >= free_timer_list_max) - { - free_timer_list_max += 256; - RECREATE (free_timer_list, uint32_t, free_timer_list_max); - memset (free_timer_list + (free_timer_list_max - 256), - 0, 256 * sizeof (uint32_t)); - } - free_timer_list[free_timer_list_pos++] = i; - break; - case TIMER_INTERVAL: - if (DIFF_TICK (timer_data[i].tick, tick) < -1000) - { - timer_data[i].tick = tick + timer_data[i].interval; - } - else - { - timer_data[i].tick += timer_data[i].interval; - } - push_timer_heap (i); - break; + delete td; + continue; } + if (td->tick + std::chrono::seconds(1) < tick) + td->tick = tick + td->interval; + else + td->tick += td->interval; + push_timer_heap(td); } - if (nextmin < 10) - nextmin = 10; - return nextmin; + return std::max(nextmin, std::chrono::milliseconds(10)); +} + +tick_t file_modified(const char *name) +{ + struct stat buf; + if (stat(name, &buf)) + return tick_t(); + return tick_t(std::chrono::seconds(buf.st_mtime)); } |