diff options
Diffstat (limited to 'src/civetweb/src/timer.inl')
-rw-r--r-- | src/civetweb/src/timer.inl | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/src/civetweb/src/timer.inl b/src/civetweb/src/timer.inl new file mode 100644 index 000000000..deca4ac88 --- /dev/null +++ b/src/civetweb/src/timer.inl @@ -0,0 +1,227 @@ +/* This file is part of the CivetWeb web server. + * See https://github.com/civetweb/civetweb/ + * (C) 2014-2017 by the CivetWeb authors, MIT license. + */ + +#if !defined(MAX_TIMERS) +#define MAX_TIMERS MAX_WORKER_THREADS +#endif + +typedef int (*taction)(void *arg); + +struct ttimer { + double time; + double period; + taction action; + void *arg; +}; + +struct ttimers { + pthread_t threadid; /* Timer thread ID */ + pthread_mutex_t mutex; /* Protects timer lists */ + struct ttimer timers[MAX_TIMERS]; /* List of timers */ + unsigned timer_count; /* Current size of timer list */ +}; + + +TIMER_API double +timer_getcurrenttime(void) +{ +#if defined(_WIN32) + /* GetTickCount returns milliseconds since system start as + * unsigned 32 bit value. It will wrap around every 49.7 days. + * We need to use a 64 bit counter (will wrap in 500 mio. years), + * by adding the 32 bit difference since the last call to a + * 64 bit counter. This algorithm will only work, if this + * function is called at least once every 7 weeks. */ + static DWORD last_tick; + static uint64_t now_tick64; + + DWORD now_tick = GetTickCount(); + + now_tick64 += ((DWORD)(now_tick - last_tick)); + last_tick = now_tick; + return (double)now_tick64 * 1.0E-3; +#else + struct timespec now_ts; + + clock_gettime(CLOCK_MONOTONIC, &now_ts); + return (double)now_ts.tv_sec + (double)now_ts.tv_nsec * 1.0E-9; +#endif +} + + +TIMER_API int +timer_add(struct mg_context *ctx, + double next_time, + double period, + int is_relative, + taction action, + void *arg) +{ + unsigned u, v; + int error = 0; + double now; + + if (ctx->stop_flag) { + return 0; + } + + now = timer_getcurrenttime(); + + /* HCP24: if is_relative = 0 and next_time < now + * action will be called so fast as possible + * if additional period > 0 + * action will be called so fast as possible + * n times until (next_time + (n * period)) > now + * then the period is working + * Solution: + * if next_time < now then we set next_time = now. + * The first callback will be so fast as possible (now) + * but the next callback on period + */ + if (is_relative) { + next_time += now; + } + + /* You can not set timers into the past */ + if (next_time < now) { + next_time = now; + } + + pthread_mutex_lock(&ctx->timers->mutex); + if (ctx->timers->timer_count == MAX_TIMERS) { + error = 1; + } else { + /* Insert new timer into a sorted list. */ + /* The linear list is still most efficient for short lists (small + * number of timers) - if there are many timers, different + * algorithms will work better. */ + for (u = 0; u < ctx->timers->timer_count; u++) { + if (ctx->timers->timers[u].time > next_time) { + /* HCP24: moving all timers > next_time */ + for (v = ctx->timers->timer_count; v > u; v--) { + ctx->timers->timers[v] = ctx->timers->timers[v - 1]; + } + break; + } + } + ctx->timers->timers[u].time = next_time; + ctx->timers->timers[u].period = period; + ctx->timers->timers[u].action = action; + ctx->timers->timers[u].arg = arg; + ctx->timers->timer_count++; + } + pthread_mutex_unlock(&ctx->timers->mutex); + return error; +} + + +static void +timer_thread_run(void *thread_func_param) +{ + struct mg_context *ctx = (struct mg_context *)thread_func_param; + double d; + unsigned u; + int re_schedule; + struct ttimer t; + + mg_set_thread_name("timer"); + + if (ctx->callbacks.init_thread) { + /* Timer thread */ + ctx->callbacks.init_thread(ctx, 2); + } + + d = timer_getcurrenttime(); + + while (ctx->stop_flag == 0) { + pthread_mutex_lock(&ctx->timers->mutex); + if ((ctx->timers->timer_count > 0) + && (d >= ctx->timers->timers[0].time)) { + t = ctx->timers->timers[0]; + for (u = 1; u < ctx->timers->timer_count; u++) { + ctx->timers->timers[u - 1] = ctx->timers->timers[u]; + } + ctx->timers->timer_count--; + pthread_mutex_unlock(&ctx->timers->mutex); + re_schedule = t.action(t.arg); + if (re_schedule && (t.period > 0)) { + timer_add(ctx, t.time + t.period, t.period, 0, t.action, t.arg); + } + continue; + } else { + pthread_mutex_unlock(&ctx->timers->mutex); + } + +/* 10 ms seems reasonable. + * A faster loop (smaller sleep value) increases CPU load, + * a slower loop (higher sleep value) decreases timer accuracy. + */ +#ifdef _WIN32 + Sleep(10); +#else + usleep(10000); +#endif + + d = timer_getcurrenttime(); + } + + pthread_mutex_lock(&ctx->timers->mutex); + ctx->timers->timer_count = 0; + pthread_mutex_unlock(&ctx->timers->mutex); +} + + +#ifdef _WIN32 +static unsigned __stdcall timer_thread(void *thread_func_param) +{ + timer_thread_run(thread_func_param); + return 0; +} +#else +static void * +timer_thread(void *thread_func_param) +{ + timer_thread_run(thread_func_param); + return NULL; +} +#endif /* _WIN32 */ + + +TIMER_API int +timers_init(struct mg_context *ctx) +{ + ctx->timers = + (struct ttimers *)mg_calloc_ctx(sizeof(struct ttimers), 1, ctx); + (void)pthread_mutex_init(&ctx->timers->mutex, NULL); + + (void)timer_getcurrenttime(); + + /* Start timer thread */ + mg_start_thread_with_id(timer_thread, ctx, &ctx->timers->threadid); + + return 0; +} + + +TIMER_API void +timers_exit(struct mg_context *ctx) +{ + if (ctx->timers) { + pthread_mutex_lock(&ctx->timers->mutex); + ctx->timers->timer_count = 0; + + mg_join_thread(ctx->timers->threadid); + + /* TODO: Do we really need to unlock the mutex, before + * destroying it, if it's destroyed by the thread currently + * owning the mutex? */ + pthread_mutex_unlock(&ctx->timers->mutex); + (void)pthread_mutex_destroy(&ctx->timers->mutex); + mg_free(ctx->timers); + } +} + + +/* End of timer.inl */ |