blob: 73083f1609f4b01e152401390a32163796ec9818 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file tmr.c Timer implementation
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#ifdef HAVE_SYS_TIME_H
8#include <sys/time.h>
9#endif
10#ifdef WIN32
11#include <windows.h>
12#else
13#include <time.h>
14#endif
15#ifdef HAVE_PTHREAD
16#include <stdlib.h>
17#include <pthread.h>
18#endif
19#include <re_types.h>
20#include <re_list.h>
21#include <re_fmt.h>
22#include <re_mem.h>
23#include <re_tmr.h>
24
25
26#define DEBUG_MODULE "tmr"
27#define DEBUG_LEVEL 5
28#include <re_dbg.h>
29
30
31#if !defined (RELEASE) && !defined (TMR_DEBUG)
32#define TMR_DEBUG 1 /**< Timer debugging (0 or 1) */
33#endif
34
35/** Timer values */
36enum {
37 MAX_BLOCKING = 100 /**< Maximum time spent in handler [ms] */
38};
39
40extern struct list *tmrl_get(void);
41
42
43static bool inspos_handler(struct le *le, void *arg)
44{
45 struct tmr *tmr = le->data;
46 const uint64_t now = *(uint64_t *)arg;
47
48 return tmr->jfs <= now;
49}
50
51
52static bool inspos_handler_0(struct le *le, void *arg)
53{
54 struct tmr *tmr = le->data;
55 const uint64_t now = *(uint64_t *)arg;
56
57 return tmr->jfs > now;
58}
59
60
61#if TMR_DEBUG
62static void call_handler(tmr_h *th, void *arg)
63{
64 const uint64_t tick = tmr_jiffies();
65 uint32_t diff;
66
67 /* Call handler */
68 th(arg);
69
70 diff = (uint32_t)(tmr_jiffies() - tick);
71
72 if (diff > MAX_BLOCKING) {
73 DEBUG_WARNING("long async blocking: %u>%u ms (h=%p arg=%p)\n",
74 diff, MAX_BLOCKING, th, arg);
75 }
76}
77#endif
78
79
80/**
81 * Poll all timers in the current thread
82 *
83 * @param tmrl Timer list
84 */
85void tmr_poll(struct list *tmrl)
86{
87 const uint64_t jfs = tmr_jiffies();
88
89 for (;;) {
90 struct tmr *tmr;
91 tmr_h *th;
92 void *th_arg;
93
94 tmr = list_ledata(tmrl->head);
95
96 if (!tmr || (tmr->jfs > jfs)) {
97 break;
98 }
99
100 th = tmr->th;
101 th_arg = tmr->arg;
102
103 tmr->th = NULL;
104
105 list_unlink(&tmr->le);
106
107 if (!th)
108 continue;
109
110#if TMR_DEBUG
111 call_handler(th, th_arg);
112#else
113 th(th_arg);
114#endif
115 }
116}
117
118
119/**
120 * Get the timer jiffies in milliseconds
121 *
122 * @return Jiffies in [ms]
123 */
124uint64_t tmr_jiffies(void)
125{
126 uint64_t jfs;
127
128#if defined(WIN32)
129 FILETIME ft;
130 ULARGE_INTEGER li;
131 GetSystemTimeAsFileTime(&ft);
132 li.LowPart = ft.dwLowDateTime;
133 li.HighPart = ft.dwHighDateTime;
134 jfs = li.QuadPart/10/1000;
135#else
136 struct timeval now;
137
138 if (0 != gettimeofday(&now, NULL)) {
139 DEBUG_WARNING("jiffies: gettimeofday() failed (%m)\n", errno);
140 return 0;
141 }
142
143 jfs = (long)now.tv_sec * (uint64_t)1000;
144 jfs += now.tv_usec / 1000;
145#endif
146
147 return jfs;
148}
149
150
151/**
152 * Get number of milliseconds until the next timer expires
153 *
154 * @param tmrl Timer-list
155 *
156 * @return Number of [ms], or 0 if no active timers
157 */
158uint64_t tmr_next_timeout(struct list *tmrl)
159{
160 const uint64_t jif = tmr_jiffies();
161 const struct tmr *tmr;
162
163 tmr = list_ledata(tmrl->head);
164 if (!tmr)
165 return 0;
166
167 if (tmr->jfs <= jif)
168 return 1;
169 else
170 return tmr->jfs - jif;
171}
172
173
174int tmr_status(struct re_printf *pf, void *unused)
175{
176 struct list *tmrl = tmrl_get();
177 struct le *le;
178 uint32_t n;
179 int err;
180
181 (void)unused;
182
183 n = list_count(tmrl);
184 if (!n)
185 return 0;
186
187 err = re_hprintf(pf, "Timers (%u):\n", n);
188
189 for (le = tmrl->head; le; le = le->next) {
190 const struct tmr *tmr = le->data;
191
192 err |= re_hprintf(pf, " %p: th=%p expire=%llums\n",
193 tmr, tmr->th,
194 (unsigned long long)tmr_get_expire(tmr));
195 }
196
197 if (n > 100)
198 err |= re_hprintf(pf, " (Dumped Timers: %u)\n", n);
199
200 return err;
201}
202
203
204/**
205 * Print timer debug info to stderr
206 */
207void tmr_debug(void)
208{
209 if (!list_isempty(tmrl_get()))
210 (void)re_fprintf(stderr, "%H", tmr_status, NULL);
211}
212
213
214/**
215 * Initialise a timer object
216 *
217 * @param tmr Timer to initialise
218 */
219void tmr_init(struct tmr *tmr)
220{
221 if (!tmr)
222 return;
223
224 memset(tmr, 0, sizeof(*tmr));
225}
226
227
228/**
229 * Start a timer
230 *
231 * @param tmr Timer to start
232 * @param delay Timer delay in [ms]
233 * @param th Timeout handler
234 * @param arg Handler argument
235 */
236void tmr_start(struct tmr *tmr, uint64_t delay, tmr_h *th, void *arg)
237{
238 struct list *tmrl = tmrl_get();
239 struct le *le;
240
241 if (!tmr)
242 return;
243
244 if (tmr->th) {
245 list_unlink(&tmr->le);
246 }
247
248 tmr->th = th;
249 tmr->arg = arg;
250
251 if (!th)
252 return;
253
254 tmr->jfs = delay + tmr_jiffies();
255
256 if (delay == 0) {
257 le = list_apply(tmrl, true, inspos_handler_0, &tmr->jfs);
258 if (le) {
259 list_insert_before(tmrl, le, &tmr->le, tmr);
260 }
261 else {
262 list_append(tmrl, &tmr->le, tmr);
263 }
264 }
265 else {
266 le = list_apply(tmrl, false, inspos_handler, &tmr->jfs);
267 if (le) {
268 list_insert_after(tmrl, le, &tmr->le, tmr);
269 }
270 else {
271 list_prepend(tmrl, &tmr->le, tmr);
272 }
273 }
274}
275
276
277/**
278 * Cancel an active timer
279 *
280 * @param tmr Timer to cancel
281 */
282void tmr_cancel(struct tmr *tmr)
283{
284 tmr_start(tmr, 0, NULL, NULL);
285}
286
287
288/**
289 * Get the time left until timer expires
290 *
291 * @param tmr Timer object
292 *
293 * @return Time in [ms] until expiration
294 */
295uint64_t tmr_get_expire(const struct tmr *tmr)
296{
297 uint64_t jfs;
298
299 if (!tmr || !tmr->th)
300 return 0;
301
302 jfs = tmr_jiffies();
303
304 return (tmr->jfs > jfs) ? (tmr->jfs - jfs) : 0;
305}