timer.inl 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /* This file is part of the CivetWeb web server.
  2. * See https://github.com/civetweb/civetweb/
  3. * (C) 2014-2021 by the CivetWeb authors, MIT license.
  4. */
  5. #if !defined(MAX_TIMERS)
  6. #define MAX_TIMERS MAX_WORKER_THREADS
  7. #endif
  8. #if !defined(TIMER_RESOLUTION)
  9. /* Timer resolution in ms */
  10. #define TIMER_RESOLUTION (10)
  11. #endif
  12. typedef int (*taction)(void *arg);
  13. typedef void (*tcancelaction)(void *arg);
  14. struct ttimer {
  15. double time;
  16. double period;
  17. taction action;
  18. void *arg;
  19. tcancelaction cancel;
  20. };
  21. struct ttimers {
  22. pthread_t threadid; /* Timer thread ID */
  23. pthread_mutex_t mutex; /* Protects timer lists */
  24. struct ttimer *timers; /* List of timers */
  25. unsigned timer_count; /* Current size of timer list */
  26. unsigned timer_capacity; /* Capacity of timer list */
  27. #if defined(_WIN32)
  28. DWORD last_tick;
  29. uint64_t now_tick64;
  30. #endif
  31. };
  32. TIMER_API double
  33. timer_getcurrenttime(struct mg_context *ctx)
  34. {
  35. #if defined(_WIN32)
  36. uint64_t now_tick64 = 0;
  37. #if defined(_WIN64)
  38. now_tick64 = GetTickCount64();
  39. #else
  40. /* GetTickCount returns milliseconds since system start as
  41. * unsigned 32 bit value. It will wrap around every 49.7 days.
  42. * We need to use a 64 bit counter (will wrap in 500 mio. years),
  43. * by adding the 32 bit difference since the last call to a
  44. * 64 bit counter. This algorithm will only work, if this
  45. * function is called at least once every 7 weeks. */
  46. DWORD now_tick = GetTickCount();
  47. if (ctx->timers) {
  48. pthread_mutex_lock(&ctx->timers->mutex);
  49. ctx->timers->now_tick64 += now_tick - ctx->timers->last_tick;
  50. now_tick64 = ctx->timers->now_tick64;
  51. ctx->timers->last_tick = now_tick;
  52. pthread_mutex_unlock(&ctx->timers->mutex);
  53. }
  54. #endif
  55. return (double)now_tick64 * 1.0E-3;
  56. #else
  57. struct timespec now_ts;
  58. (void)ctx;
  59. clock_gettime(CLOCK_MONOTONIC, &now_ts);
  60. return (double)now_ts.tv_sec + (double)now_ts.tv_nsec * 1.0E-9;
  61. #endif
  62. }
  63. TIMER_API int
  64. timer_add(struct mg_context *ctx,
  65. double next_time,
  66. double period,
  67. int is_relative,
  68. taction action,
  69. void *arg,
  70. tcancelaction cancel)
  71. {
  72. int error = 0;
  73. double now;
  74. if (!ctx->timers) {
  75. return 1;
  76. }
  77. now = timer_getcurrenttime(ctx);
  78. /* HCP24: if is_relative = 0 and next_time < now
  79. * action will be called so fast as possible
  80. * if additional period > 0
  81. * action will be called so fast as possible
  82. * n times until (next_time + (n * period)) > now
  83. * then the period is working
  84. * Solution:
  85. * if next_time < now then we set next_time = now.
  86. * The first callback will be so fast as possible (now)
  87. * but the next callback on period
  88. */
  89. if (is_relative) {
  90. next_time += now;
  91. }
  92. /* You can not set timers into the past */
  93. if (next_time < now) {
  94. next_time = now;
  95. }
  96. pthread_mutex_lock(&ctx->timers->mutex);
  97. if (ctx->timers->timer_count == MAX_TIMERS) {
  98. error = 1;
  99. } else if (ctx->timers->timer_count == ctx->timers->timer_capacity) {
  100. unsigned capacity = (ctx->timers->timer_capacity * 2) + 1;
  101. struct ttimer *timers =
  102. (struct ttimer *)mg_realloc_ctx(ctx->timers->timers,
  103. capacity * sizeof(struct ttimer),
  104. ctx);
  105. if (timers) {
  106. ctx->timers->timers = timers;
  107. ctx->timers->timer_capacity = capacity;
  108. } else {
  109. error = 1;
  110. }
  111. }
  112. if (!error) {
  113. /* Insert new timer into a sorted list. */
  114. /* The linear list is still most efficient for short lists (small
  115. * number of timers) - if there are many timers, different
  116. * algorithms will work better. */
  117. unsigned u = ctx->timers->timer_count;
  118. for (; (u > 0) && (ctx->timers->timers[u - 1].time > next_time); u--) {
  119. ctx->timers->timers[u] = ctx->timers->timers[u - 1];
  120. }
  121. ctx->timers->timers[u].time = next_time;
  122. ctx->timers->timers[u].period = period;
  123. ctx->timers->timers[u].action = action;
  124. ctx->timers->timers[u].arg = arg;
  125. ctx->timers->timers[u].cancel = cancel;
  126. ctx->timers->timer_count++;
  127. }
  128. pthread_mutex_unlock(&ctx->timers->mutex);
  129. return error;
  130. }
  131. static void
  132. timer_thread_run(void *thread_func_param)
  133. {
  134. struct mg_context *ctx = (struct mg_context *)thread_func_param;
  135. double d;
  136. unsigned u;
  137. int action_res;
  138. struct ttimer t;
  139. mg_set_thread_name("timer");
  140. if (ctx->callbacks.init_thread) {
  141. /* Timer thread */
  142. ctx->callbacks.init_thread(ctx, 2);
  143. }
  144. /* Timer main loop */
  145. d = timer_getcurrenttime(ctx);
  146. while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
  147. pthread_mutex_lock(&ctx->timers->mutex);
  148. if ((ctx->timers->timer_count > 0)
  149. && (d >= ctx->timers->timers[0].time)) {
  150. /* Timer list is sorted. First action should run now. */
  151. /* Store active timer in "t" */
  152. t = ctx->timers->timers[0];
  153. /* Shift all other timers */
  154. for (u = 1; u < ctx->timers->timer_count; u++) {
  155. ctx->timers->timers[u - 1] = ctx->timers->timers[u];
  156. }
  157. ctx->timers->timer_count--;
  158. pthread_mutex_unlock(&ctx->timers->mutex);
  159. /* Call timer action */
  160. action_res = t.action(t.arg);
  161. /* action_res == 1: reschedule */
  162. /* action_res == 0: do not reschedule, free(arg) */
  163. if ((action_res > 0) && (t.period > 0)) {
  164. /* Should schedule timer again */
  165. timer_add(ctx,
  166. t.time + t.period,
  167. t.period,
  168. 0,
  169. t.action,
  170. t.arg,
  171. t.cancel);
  172. } else {
  173. /* Allow user to free timer argument */
  174. if (t.cancel != NULL) {
  175. t.cancel(t.arg);
  176. }
  177. }
  178. continue;
  179. } else {
  180. pthread_mutex_unlock(&ctx->timers->mutex);
  181. }
  182. /* TIMER_RESOLUTION = 10 ms seems reasonable.
  183. * A faster loop (smaller sleep value) increases CPU load,
  184. * a slower loop (higher sleep value) decreases timer accuracy.
  185. */
  186. mg_sleep(TIMER_RESOLUTION);
  187. d = timer_getcurrenttime(ctx);
  188. }
  189. /* Remove remaining timers */
  190. for (u = 0; u < ctx->timers->timer_count; u++) {
  191. t = ctx->timers->timers[u];
  192. if (t.cancel != NULL) {
  193. t.cancel(t.arg);
  194. }
  195. }
  196. }
  197. #if defined(_WIN32)
  198. static unsigned __stdcall timer_thread(void *thread_func_param)
  199. {
  200. timer_thread_run(thread_func_param);
  201. return 0;
  202. }
  203. #else
  204. static void *
  205. timer_thread(void *thread_func_param)
  206. {
  207. struct sigaction sa;
  208. /* Ignore SIGPIPE */
  209. memset(&sa, 0, sizeof(sa));
  210. sa.sa_handler = SIG_IGN;
  211. sigaction(SIGPIPE, &sa, NULL);
  212. timer_thread_run(thread_func_param);
  213. return NULL;
  214. }
  215. #endif /* _WIN32 */
  216. TIMER_API int
  217. timers_init(struct mg_context *ctx)
  218. {
  219. /* Initialize timers data structure */
  220. ctx->timers =
  221. (struct ttimers *)mg_calloc_ctx(sizeof(struct ttimers), 1, ctx);
  222. if (!ctx->timers) {
  223. return -1;
  224. }
  225. ctx->timers->timers = NULL;
  226. /* Initialize mutex */
  227. if (0 != pthread_mutex_init(&ctx->timers->mutex, NULL)) {
  228. mg_free(ctx->timers);
  229. ctx->timers = NULL;
  230. return -1;
  231. }
  232. /* For some systems timer_getcurrenttime does some initialization
  233. * during the first call. Call it once now, ignore the result. */
  234. (void)timer_getcurrenttime(ctx);
  235. /* Start timer thread */
  236. if (mg_start_thread_with_id(timer_thread, ctx, &ctx->timers->threadid)
  237. != 0) {
  238. (void)pthread_mutex_destroy(&ctx->timers->mutex);
  239. mg_free(ctx->timers);
  240. ctx->timers = NULL;
  241. return -1;
  242. }
  243. return 0;
  244. }
  245. TIMER_API void
  246. timers_exit(struct mg_context *ctx)
  247. {
  248. if (ctx->timers) {
  249. mg_join_thread(ctx->timers->threadid);
  250. (void)pthread_mutex_destroy(&ctx->timers->mutex);
  251. mg_free(ctx->timers->timers);
  252. mg_free(ctx->timers);
  253. ctx->timers = NULL;
  254. }
  255. }
  256. /* End of timer.inl */