Lely core libraries 2.3.4
timer.c
Go to the documentation of this file.
1
24#include "io.h"
25
26#if !LELY_NO_STDIO && _WIN32
27
28#include "../timer.h"
29#include <lely/io2/ctx.h>
30#include <lely/io2/sys/clock.h>
31#include <lely/io2/sys/timer.h>
32#include <lely/io2/win32/poll.h>
33#include <lely/util/errnum.h>
34#include <lely/util/time.h>
35#include <lely/util/util.h>
36
37#include <assert.h>
38#include <stdlib.h>
39
40static io_ctx_t *io_timer_impl_dev_get_ctx(const io_dev_t *dev);
41static ev_exec_t *io_timer_impl_dev_get_exec(const io_dev_t *dev);
42static size_t io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task);
43static size_t io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task);
44
45// clang-format off
46static const struct io_dev_vtbl io_timer_impl_dev_vtbl = {
47 &io_timer_impl_dev_get_ctx,
48 &io_timer_impl_dev_get_exec,
49 &io_timer_impl_dev_cancel,
50 &io_timer_impl_dev_abort
51};
52// clang-format on
53
54static io_dev_t *io_timer_impl_get_dev(const io_timer_t *timer);
55static io_clock_t *io_timer_impl_get_clock(const io_timer_t *timer);
56static int io_timer_impl_getoverrun(const io_timer_t *timer);
57static int io_timer_impl_gettime(
58 const io_timer_t *timer, struct itimerspec *value);
59static int io_timer_impl_settime(io_timer_t *timer, int flags,
60 const struct itimerspec *value, struct itimerspec *ovalue);
61static void io_timer_impl_submit_wait(
62 io_timer_t *timer, struct io_timer_wait *wait);
63
64// clang-format off
65static const struct io_timer_vtbl io_timer_impl_vtbl = {
66 &io_timer_impl_get_dev,
67 &io_timer_impl_get_clock,
68 &io_timer_impl_getoverrun,
69 &io_timer_impl_gettime,
70 &io_timer_impl_settime,
71 &io_timer_impl_submit_wait
72};
73// clang-format on
74
75static void io_timer_impl_svc_shutdown(struct io_svc *svc);
76
77// clang-format off
78static const struct io_svc_vtbl io_timer_impl_svc_vtbl = {
79 NULL,
80 &io_timer_impl_svc_shutdown
81};
82// clang-format on
83
84struct io_timer_impl {
85 const struct io_dev_vtbl *dev_vptr;
86 const struct io_timer_vtbl *timer_vptr;
87 struct io_svc svc;
88 io_ctx_t *ctx;
89 ev_exec_t *exec;
90 clockid_t clockid;
91#if !LELY_NO_THREADS
92 CRITICAL_SECTION CriticalSection1;
93#endif
94 int shutdown;
95 struct sllist queue;
96#if !LELY_NO_THREADS
97 CRITICAL_SECTION CriticalSection2;
98#endif
99 struct itimerspec value;
100 int overrun;
101#if !LELY_NO_THREADS
102 CRITICAL_SECTION CriticalSection3;
103#endif
104 HANDLE Timer;
105};
106
107static void CALLBACK io_timer_impl_func(
108 PVOID lpParameter, BOOLEAN TimerOrWaitFired);
109
110static inline struct io_timer_impl *io_timer_impl_from_dev(const io_dev_t *dev);
111static inline struct io_timer_impl *io_timer_impl_from_timer(
112 const io_timer_t *timer);
113static inline struct io_timer_impl *io_timer_impl_from_svc(
114 const struct io_svc *svc);
115
116static void io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
117 struct ev_task *task);
118
119void *
120io_timer_alloc(void)
121{
122 struct io_timer_impl *impl = malloc(sizeof(*impl));
123 if (!impl) {
124 set_errc(errno2c(errno));
125 return NULL;
126 }
127 return &impl->timer_vptr;
128}
129
130void
131io_timer_free(void *ptr)
132{
133 if (ptr)
134 free(io_timer_impl_from_timer(ptr));
135}
136
138io_timer_init(io_timer_t *timer, io_poll_t *poll, ev_exec_t *exec,
139 clockid_t clockid)
140{
141 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
142 assert(poll);
143 assert(exec);
144 io_ctx_t *ctx = io_poll_get_ctx(poll);
145 assert(ctx);
146
147 impl->dev_vptr = &io_timer_impl_dev_vtbl;
148 impl->timer_vptr = &io_timer_impl_vtbl;
149
150 impl->svc = (struct io_svc)IO_SVC_INIT(&io_timer_impl_svc_vtbl);
151 impl->ctx = ctx;
152
153 impl->exec = exec;
154
155 impl->clockid = clockid;
156
157#if !LELY_NO_THREADS
158 InitializeCriticalSection(&impl->CriticalSection1);
159#endif
160 impl->shutdown = 0;
161 sllist_init(&impl->queue);
162
163#if !LELY_NO_THREADS
164 InitializeCriticalSection(&impl->CriticalSection2);
165#endif
166 impl->value = (struct itimerspec){ { 0, 0 }, { 0, 0 } };
167 impl->overrun = 0;
168
169#if !LELY_NO_THREADS
170 InitializeCriticalSection(&impl->CriticalSection3);
171#endif
172 impl->Timer = NULL;
173
174 io_ctx_insert(impl->ctx, &impl->svc);
175
176 return timer;
177}
178
179void
180io_timer_fini(io_timer_t *timer)
181{
182 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
183
184 io_ctx_remove(impl->ctx, &impl->svc);
185 // Cancel all pending tasks.
186 io_timer_impl_svc_shutdown(&impl->svc);
187
188 if (impl->Timer)
189 DeleteTimerQueueTimer(NULL, impl->Timer, INVALID_HANDLE_VALUE);
190
191#if !LELY_NO_THREADS
192 DeleteCriticalSection(&impl->CriticalSection3);
193 DeleteCriticalSection(&impl->CriticalSection2);
194 DeleteCriticalSection(&impl->CriticalSection1);
195#endif
196}
197
199io_timer_create(io_poll_t *poll, ev_exec_t *exec, clockid_t clockid)
200{
201 DWORD dwErrCode = 0;
202
203 io_timer_t *timer = io_timer_alloc();
204 if (!timer) {
205 dwErrCode = GetLastError();
206 goto error_alloc;
207 }
208
209 io_timer_t *tmp = io_timer_init(timer, poll, exec, clockid);
210 if (!tmp) {
211 dwErrCode = GetLastError();
212 goto error_init;
213 }
214 timer = tmp;
215
216 return timer;
217
218error_init:
219 io_timer_free((void *)timer);
220error_alloc:
221 SetLastError(dwErrCode);
222 return NULL;
223}
224
225void
227{
228 if (timer) {
229 io_timer_fini(timer);
230 io_timer_free((void *)timer);
231 }
232}
233
234static io_ctx_t *
235io_timer_impl_dev_get_ctx(const io_dev_t *dev)
236{
237 const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
238
239 return impl->ctx;
240}
241
242static ev_exec_t *
243io_timer_impl_dev_get_exec(const io_dev_t *dev)
244{
245 const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
246
247 return impl->exec;
248}
249
250static size_t
251io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task)
252{
253 struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
254
255 struct sllist queue;
256 sllist_init(&queue);
257
258 io_timer_impl_pop(impl, &queue, task);
259
260 return io_timer_wait_queue_post(&queue, -1, ERROR_OPERATION_ABORTED);
261}
262
263static size_t
264io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task)
265{
266 struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
267
268 struct sllist queue;
269 sllist_init(&queue);
270
271 io_timer_impl_pop(impl, &queue, task);
272
273 return ev_task_queue_abort(&queue);
274}
275
276static io_dev_t *
277io_timer_impl_get_dev(const io_timer_t *timer)
278{
279 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
280
281 return &impl->dev_vptr;
282}
283
284static io_clock_t *
285io_timer_impl_get_clock(const io_timer_t *timer)
286{
287 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
288 assert(impl->clockid == CLOCK_REALTIME
289 || impl->clockid == CLOCK_MONOTONIC);
290
291 switch (impl->clockid) {
292 case CLOCK_REALTIME: return IO_CLOCK_REALTIME;
293 case CLOCK_MONOTONIC: return IO_CLOCK_MONOTONIC;
294 default: return NULL;
295 }
296}
297
298static int
299io_timer_impl_getoverrun(const io_timer_t *timer)
300{
301 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
302
303#if !LELY_NO_THREADS
304 EnterCriticalSection((LPCRITICAL_SECTION)&impl->CriticalSection2);
305#endif
306 int overrun = impl->overrun;
307#if !LELY_NO_THREADS
308 LeaveCriticalSection((LPCRITICAL_SECTION)&impl->CriticalSection2);
309#endif
310
311 return overrun;
312}
313
314static int
315io_timer_impl_gettime(const io_timer_t *timer, struct itimerspec *value)
316{
317 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
318 assert(value);
319
320#if !LELY_NO_THREADS
321 EnterCriticalSection((LPCRITICAL_SECTION)&impl->CriticalSection2);
322#endif
323 struct itimerspec value_ = impl->value;
324#if !LELY_NO_THREADS
325 LeaveCriticalSection((LPCRITICAL_SECTION)&impl->CriticalSection2);
326#endif
327
328 if (value_.it_value.tv_sec || value_.it_value.tv_nsec) {
329 struct timespec now = { 0, 0 };
330 if (clock_gettime(impl->clockid, &now) == -1) {
331 set_errc(errno2c(errno));
332 return -1;
333 }
334 timespec_sub(&value_.it_value, &now);
335 }
336
337 if (value)
338 *value = value_;
339
340 return 0;
341}
342
343static int
344io_timer_impl_settime(io_timer_t *timer, int flags,
345 const struct itimerspec *value, struct itimerspec *ovalue)
346{
347 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
348 assert(value);
349
350 struct timespec now = { 0, 0 };
351 if (clock_gettime(impl->clockid, &now) == -1) {
352 set_errc(errno2c(errno));
353 return -1;
354 }
355
356 ULONGLONG DueTime = 0;
357 ULONGLONG Period = 0;
358
359 struct timespec period = value->it_interval;
360 struct timespec expiry = value->it_value;
361 int arm = expiry.tv_sec || expiry.tv_nsec;
362 if (arm) {
363 if (period.tv_nsec < 0 || period.tv_nsec >= 1000000000l) {
364 SetLastError(ERROR_INVALID_PARAMETER);
365 return -1;
366 }
367 if (period.tv_sec < 0)
368 period = (struct timespec){ 0, 0 };
369 Period = (ULONGLONG)period.tv_sec * 1000
370 + (period.tv_nsec + 999999l) / 1000000l;
371 period.tv_nsec = (Period % 1000) * 1000000l;
372 if (Period > ULONG_MAX) {
373 SetLastError(ERROR_INVALID_PARAMETER);
374 return -1;
375 }
376
377 if (expiry.tv_nsec < 0 || expiry.tv_nsec >= 1000000000l) {
378 SetLastError(ERROR_INVALID_PARAMETER);
379 return -1;
380 }
381 if (flags & TIMER_ABSTIME)
382 timespec_sub(&expiry, &now);
383 if (expiry.tv_sec < 0)
384 expiry = (struct timespec){ 0, 0 };
385 DueTime = (ULONGLONG)expiry.tv_sec * 1000
386 + (expiry.tv_nsec + 999999l) / 1000000l;
387 if (DueTime > ULONG_MAX) {
388 SetLastError(ERROR_INVALID_PARAMETER);
389 return -1;
390 }
391
392 timespec_add(&expiry, &now);
393 } else {
394 period = (struct timespec){ 0, 0 };
395 }
396
397 int result = 0;
398 DWORD dwErrCode = GetLastError();
399
400#if !LELY_NO_THREADS
401 EnterCriticalSection(&impl->CriticalSection3);
402#endif
403
404 if (impl->Timer) {
405 DeleteTimerQueueTimer(NULL, impl->Timer, INVALID_HANDLE_VALUE);
406 impl->Timer = NULL;
407 }
408
409#if !LELY_NO_THREADS
410 EnterCriticalSection(&impl->CriticalSection2);
411#endif
412
413 struct itimerspec ovalue_ = impl->value;
414 if (ovalue_.it_value.tv_sec || ovalue_.it_value.tv_nsec)
415 timespec_sub(&ovalue_.it_value, &now);
416 impl->value = (struct itimerspec){ period, expiry };
417 impl->overrun = 0;
418
419 // TODO: Check if WT_EXECUTEDEFAULT is the best option.
420 ULONG Flags = WT_EXECUTEDEFAULT;
421 if (!Period)
422 Flags |= WT_EXECUTEONLYONCE;
423
424 // clang-format off
425 if (arm && !CreateTimerQueueTimer(&impl->Timer, NULL,
426 &io_timer_impl_func, impl, DueTime, Period, Flags)) {
427 // clang-format on
428 result = -1;
429 dwErrCode = GetLastError();
430
431 impl->value = (struct itimerspec){ { 0, 0 }, { 0, 0 } };
432 }
433
434#if !LELY_NO_THREADS
435 LeaveCriticalSection(&impl->CriticalSection2);
436 LeaveCriticalSection(&impl->CriticalSection3);
437#endif
438
439 if (ovalue)
440 *ovalue = ovalue_;
441
442 SetLastError(dwErrCode);
443 return result;
444}
445
446static void
447io_timer_impl_submit_wait(io_timer_t *timer, struct io_timer_wait *wait)
448{
449 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
450 assert(wait);
451 struct ev_task *task = &wait->task;
452
453 if (!task->exec)
454 task->exec = impl->exec;
456
457#if !LELY_NO_THREADS
458 EnterCriticalSection(&impl->CriticalSection1);
459#endif
460 if (impl->shutdown) {
461#if !LELY_NO_THREADS
462 LeaveCriticalSection(&impl->CriticalSection1);
463#endif
464 io_timer_wait_post(wait, -1, ERROR_OPERATION_ABORTED);
465 } else {
466 sllist_push_back(&impl->queue, &task->_node);
467#if !LELY_NO_THREADS
468 LeaveCriticalSection(&impl->CriticalSection1);
469#endif
470 }
471}
472
473static void
474io_timer_impl_svc_shutdown(struct io_svc *svc)
475{
476 struct io_timer_impl *impl = io_timer_impl_from_svc(svc);
477 io_dev_t *dev = &impl->dev_vptr;
478
479#if !LELY_NO_THREADS
480 EnterCriticalSection(&impl->CriticalSection1);
481#endif
482 int shutdown = !impl->shutdown;
483 impl->shutdown = 1;
484#if !LELY_NO_THREADS
485 LeaveCriticalSection(&impl->CriticalSection1);
486#endif
487
488 if (shutdown)
489 io_timer_impl_dev_cancel(dev, NULL);
490}
491
492static void CALLBACK
493io_timer_impl_func(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
494{
495 struct io_timer_impl *impl = lpParameter;
496 assert(impl);
497 (void)TimerOrWaitFired;
498
499 int errsv = errno;
500 int overrun = 0;
501 int errc = 0;
502
503 struct timespec now = { 0, 0 };
504 if (clock_gettime(impl->clockid, &now) == -1) {
505 errc = errno2c(errno);
506 overrun = -1;
507 }
508
509 struct sllist queue;
510 sllist_init(&queue);
511
512#if !LELY_NO_THREADS
513 EnterCriticalSection(&impl->CriticalSection2);
514#endif
515
516 if (overrun >= 0) {
517 struct timespec *period = &impl->value.it_interval;
518 struct timespec *expiry = &impl->value.it_value;
519 if (period->tv_sec || period->tv_nsec) {
520 assert(!(period->tv_nsec % 1000000l));
521 ULONG Period = period->tv_sec * 1000
522 + period->tv_nsec / 1000000l;
523 LONGLONG overrun = timespec_diff_msec(&now, expiry)
524 / Period;
525 timespec_add_msec(expiry, (overrun + 1) * Period);
526 impl->overrun = MIN(MAX(overrun, INT_MIN), INT_MAX);
527 } else {
528 *expiry = (struct timespec){ 0, 0 };
529 impl->overrun = 0;
530 }
531 overrun = impl->overrun;
532 }
533
534 if (overrun >= 0 || errc)
535 sllist_append(&queue, &impl->queue);
536
537#if !LELY_NO_THREADS
538 LeaveCriticalSection(&impl->CriticalSection2);
539#endif
540
541 io_timer_wait_queue_post(&queue, overrun, errc);
542 errno = errsv;
543}
544
545static inline struct io_timer_impl *
546io_timer_impl_from_dev(const io_dev_t *dev)
547{
548 assert(dev);
549
550 return structof(dev, struct io_timer_impl, dev_vptr);
551}
552
553static inline struct io_timer_impl *
554io_timer_impl_from_timer(const io_timer_t *timer)
555{
556 assert(timer);
557
558 return structof(timer, struct io_timer_impl, timer_vptr);
559}
560
561static inline struct io_timer_impl *
562io_timer_impl_from_svc(const struct io_svc *svc)
563{
564 assert(svc);
565
566 return structof(svc, struct io_timer_impl, svc);
567}
568
569static void
570io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
571 struct ev_task *task)
572{
573 assert(impl);
574 assert(queue);
575
576#if !LELY_NO_THREADS
577 EnterCriticalSection(&impl->CriticalSection1);
578#endif
579 if (!task)
580 sllist_append(queue, &impl->queue);
581 else if (sllist_remove(&impl->queue, &task->_node))
582 sllist_push_back(queue, &task->_node);
583#if !LELY_NO_THREADS
584 LeaveCriticalSection(&impl->CriticalSection1);
585#endif
586}
587
588#endif // !LELY_NO_STDIO && _WIN32
const struct io_clock_vtbl *const io_clock_t
An abstract clock.
Definition clock.h:36
This header file is part of the I/O library; it contains the I/O context and service declarations.
void io_ctx_insert(io_ctx_t *ctx, struct io_svc *svc)
Registers an I/O service with an I/O context.
Definition ctx.c:126
#define IO_SVC_INIT(vptr)
The static initializer for io_svc.
Definition ctx.h:57
void io_ctx_remove(io_ctx_t *ctx, struct io_svc *svc)
Unregisters an I/O service with an I/O context.
Definition ctx.c:141
This header file is part of the utilities library; it contains the native and platform-independent er...
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition errnum.c:944
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition errnum.c:46
void ev_exec_on_task_init(ev_exec_t *exec)
Indicates to the specified executor that a task will be submitted for execution in the future.
Definition exec.h:106
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition ev.h:29
This header file is part of the I/O library; it contains the I/O system timer declarations.
const struct io_timer_vtbl *const io_timer_t
An abstract timer.
Definition timer.h:38
This is the public header file of the utilities library.
#define structof(ptr, type, member)
Obtains the address of a structure from the address of one of its members.
Definition util.h:93
#define MIN(a, b)
Returns the minimum of a and b.
Definition util.h:57
#define MAX(a, b)
Returns the maximum of a and b.
Definition util.h:65
const struct io_dev_vtbl *const io_dev_t
An abstract I/O device.
Definition dev.h:35
io_ctx_t * io_poll_get_ctx(const io_poll_t *poll)
Returns a pointer to the I/O context with which the I/O polling instance is registered.
Definition poll.c:275
This header file is part of the I/O library; it contains the I/O polling declarations for Windows.
io_timer_t * io_timer_create(io_poll_t *poll, ev_exec_t *exec, clockid_t clockid)
Creates a new I/O system timer.
Definition timer.c:243
void io_timer_destroy(io_timer_t *timer)
Destroys an I/O system timer.
Definition timer.c:270
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
Definition sllist.h:194
struct sllist * sllist_append(struct sllist *dst, struct sllist *src)
Appends the singly-linked list at src to the one at dst.
Definition sllist.h:257
struct slnode * sllist_remove(struct sllist *list, struct slnode *node)
Removes a node from a singly-linked list.
Definition sllist.c:46
void sllist_push_back(struct sllist *list, struct slnode *node)
Pushes a node to the back of a singly-linked list.
Definition sllist.h:232
This is the internal header file of the Windows-specific I/O declarations.
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
An I/O polling interface.
Definition poll.c:51
An executable task.
Definition task.h:41
ev_exec_t * exec
A pointer to the executor to which the task is (to be) submitted.
Definition task.h:43
Definition ctx.c:38
The virtual table of an I/O service.
Definition ctx.h:67
An I/O service.
Definition ctx.h:49
A wait operation suitable for use with an I/O timer.
Definition timer.h:54
struct ev_task task
The task (to be) submitted upon completion (or cancellation) of the wait operation.
Definition timer.h:59
A struct specifying an interval and initial value for a timer.
Definition time.h:100
struct timespec it_interval
The timer period.
Definition time.h:102
struct timespec it_value
The timer expiration.
Definition time.h:104
A singly-linked list.
Definition sllist.h:52
A time type with nanosecond resolution.
Definition time.h:88
long tv_nsec
Nanoseconds [0, 999999999].
Definition time.h:92
time_t tv_sec
Whole seconds (>= 0).
Definition time.h:90
This header file is part of the I/O library; it contains the standard system clock definitions.
#define IO_CLOCK_REALTIME
The io_clock_t pointer representing the POSIX realtime clock.
Definition clock.h:36
#define IO_CLOCK_MONOTONIC
The io_clock_t pointer representing the POSIX monotonic clock.
Definition clock.h:43
size_t ev_task_queue_abort(struct sllist *queue)
Aborts the tasks in queue by invoking ev_exec_on_task_fini() for each of them.
Definition task.c:55
int clockid_t
Used for clock ID type in the clock and timer functions.
Definition types.h:40
This header file is part of the utilities library; it contains the time function declarations.
void timespec_add(struct timespec *tp, const struct timespec *inc)
Adds the time interval *inc to the time at tp.
Definition time.h:118
void timespec_sub(struct timespec *tp, const struct timespec *dec)
Subtracts the time interval *dec from the time at tp.
Definition time.h:165
void timespec_add_msec(struct timespec *tp, uint_least64_t msec)
Adds msec milliseconds to the time at tp.
Definition time.h:141
int_least64_t timespec_diff_msec(const struct timespec *t1, const struct timespec *t2)
Returns the time difference (in milliseconds) between *t1 and *t2.
Definition time.h:221