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