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 && defined(__linux__)
27
28#include "../timer.h"
29#include <lely/io2/ctx.h>
30#include <lely/io2/posix/poll.h>
31#include <lely/io2/sys/clock.h>
32#include <lely/io2/sys/timer.h>
33#include <lely/util/util.h>
34
35#include <assert.h>
36#include <errno.h>
37#include <limits.h>
38#include <stdlib.h>
39
40#if !LELY_NO_THREADS
41#include <pthread.h>
42#include <sched.h>
43#endif
44#include <unistd.h>
45
46#include <sys/timerfd.h>
47
48static io_ctx_t *io_timer_impl_dev_get_ctx(const io_dev_t *dev);
49static ev_exec_t *io_timer_impl_dev_get_exec(const io_dev_t *dev);
50static size_t io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task);
51static size_t io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task);
52
53// clang-format off
54static const struct io_dev_vtbl io_timer_impl_dev_vtbl = {
55 &io_timer_impl_dev_get_ctx,
56 &io_timer_impl_dev_get_exec,
57 &io_timer_impl_dev_cancel,
58 &io_timer_impl_dev_abort
59};
60// clang-format on
61
62static io_dev_t *io_timer_impl_get_dev(const io_timer_t *timer);
63static io_clock_t *io_timer_impl_get_clock(const io_timer_t *timer);
64static int io_timer_impl_getoverrun(const io_timer_t *timer);
65static int io_timer_impl_gettime(
66 const io_timer_t *timer, struct itimerspec *value);
67static int io_timer_impl_settime(io_timer_t *timer, int flags,
68 const struct itimerspec *value, struct itimerspec *ovalue);
69static void io_timer_impl_submit_wait(
70 io_timer_t *timer, struct io_timer_wait *wait);
71
72// clang-format off
73static const struct io_timer_vtbl io_timer_impl_vtbl = {
74 &io_timer_impl_get_dev,
75 &io_timer_impl_get_clock,
76 &io_timer_impl_getoverrun,
77 &io_timer_impl_gettime,
78 &io_timer_impl_settime,
79 &io_timer_impl_submit_wait
80};
81// clang-format on
82
83static int io_timer_impl_svc_notify_fork(
84 struct io_svc *svc, enum io_fork_event e);
85static void io_timer_impl_svc_shutdown(struct io_svc *svc);
86
87// clang-format off
88static const struct io_svc_vtbl io_timer_impl_svc_vtbl = {
89 &io_timer_impl_svc_notify_fork,
90 &io_timer_impl_svc_shutdown
91};
92// clang-format on
93
95 const struct io_dev_vtbl *dev_vptr;
96 const struct io_timer_vtbl *timer_vptr;
97 io_poll_t *poll;
98 struct io_svc svc;
99 io_ctx_t *ctx;
100 ev_exec_t *exec;
101 clockid_t clockid;
102 struct io_poll_watch watch;
103 int tfd;
104 struct ev_task wait_task;
105#if !LELY_NO_THREADS
106 pthread_mutex_t mtx;
107#endif
108 unsigned shutdown : 1;
109 unsigned wait_posted : 1;
110 struct sllist wait_queue;
111 int overrun;
112};
113
114static void io_timer_impl_watch_func(struct io_poll_watch *watch, int events);
115static void io_timer_impl_wait_task_func(struct ev_task *task);
116
117static inline struct io_timer_impl *io_timer_impl_from_dev(const io_dev_t *dev);
118static inline struct io_timer_impl *io_timer_impl_from_timer(
119 const io_timer_t *timer);
120static inline struct io_timer_impl *io_timer_impl_from_svc(
121 const struct io_svc *svc);
122
123static void io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
124 struct ev_task *task);
125
126static int io_timer_impl_open(struct io_timer_impl *impl);
127static int io_timer_impl_close(struct io_timer_impl *impl);
128
129void *
130io_timer_alloc(void)
131{
132 struct io_timer_impl *impl = malloc(sizeof(*impl));
133 if (!impl)
134 return NULL;
135 // Suppress a GCC maybe-uninitialized warning.
136 impl->timer_vptr = NULL;
137 // cppcheck-suppress memleak symbolName=impl
138 return &impl->timer_vptr;
139}
140
141void
142io_timer_free(void *ptr)
143{
144 if (ptr)
145 free(io_timer_impl_from_timer(ptr));
146}
147
149io_timer_init(io_timer_t *timer, io_poll_t *poll, ev_exec_t *exec,
150 clockid_t clockid)
151{
152 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
153 assert(poll);
154 assert(exec);
155 io_ctx_t *ctx = io_poll_get_ctx(poll);
156 assert(ctx);
157
158 int errsv = 0;
159
160 impl->dev_vptr = &io_timer_impl_dev_vtbl;
161 impl->timer_vptr = &io_timer_impl_vtbl;
162
163 impl->poll = poll;
164
165 impl->svc = (struct io_svc)IO_SVC_INIT(&io_timer_impl_svc_vtbl);
166 impl->ctx = ctx;
167
168 impl->exec = exec;
169
170 impl->clockid = clockid;
171
172 impl->watch = (struct io_poll_watch)IO_POLL_WATCH_INIT(
173 &io_timer_impl_watch_func);
174 impl->tfd = -1;
175
176 impl->wait_task = (struct ev_task)EV_TASK_INIT(
177 impl->exec, &io_timer_impl_wait_task_func);
178
179#if !LELY_NO_THREADS
180 if ((errsv = pthread_mutex_init(&impl->mtx, NULL)))
181 goto error_init_mtx;
182#endif
183
184 impl->shutdown = 0;
185 impl->wait_posted = 0;
186
187 sllist_init(&impl->wait_queue);
188
189 impl->overrun = 0;
190
191 if (io_timer_impl_open(impl) == -1) {
192 errsv = errno;
193 goto error_open;
194 }
195
196 io_ctx_insert(impl->ctx, &impl->svc);
197
198 return timer;
199
200 // io_timer_impl_close(impl);
201error_open:
202#if !LELY_NO_THREADS
203 pthread_mutex_destroy(&impl->mtx);
204error_init_mtx:
205#endif
206 errno = errsv;
207 return NULL;
208}
209
210void
211io_timer_fini(io_timer_t *timer)
212{
213 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
214
215 io_ctx_remove(impl->ctx, &impl->svc);
216 // Cancel all pending tasks.
217 io_timer_impl_svc_shutdown(&impl->svc);
218
219#if !LELY_NO_THREADS
220 pthread_mutex_lock(&impl->mtx);
221 // If necessary, busy-wait until io_timer_impl_wait_task_func()
222 // completes.
223 while (impl->wait_posted) {
224 // Try to abort io_timer_impl_wait_task_func().
225 if (ev_exec_abort(impl->wait_task.exec, &impl->wait_task))
226 break;
227 pthread_mutex_unlock(&impl->mtx);
228 sched_yield();
229 pthread_mutex_lock(&impl->mtx);
230 }
231 pthread_mutex_unlock(&impl->mtx);
232#endif
233
234 // Disarm the timer.
235 io_timer_impl_close(impl);
236
237#if !LELY_NO_THREADS
238 pthread_mutex_destroy(&impl->mtx);
239#endif
240}
241
244{
245 int errsv = 0;
246
247 io_timer_t *timer = io_timer_alloc();
248 if (!timer) {
249 errsv = errno;
250 goto error_alloc;
251 }
252
253 io_timer_t *tmp = io_timer_init(timer, poll, exec, clockid);
254 if (!tmp) {
255 errsv = errno;
256 goto error_init;
257 }
258 timer = tmp;
259
260 return timer;
261
262error_init:
263 io_timer_free((void *)timer);
264error_alloc:
265 errno = errsv;
266 return NULL;
267}
268
269void
271{
272 if (timer) {
273 io_timer_fini(timer);
274 io_timer_free((void *)timer);
275 }
276}
277
278static io_ctx_t *
279io_timer_impl_dev_get_ctx(const io_dev_t *dev)
280{
281 const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
282
283 return impl->ctx;
284}
285
286static ev_exec_t *
287io_timer_impl_dev_get_exec(const io_dev_t *dev)
288{
289 const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
290
291 return impl->exec;
292}
293
294static size_t
295io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task)
296{
297 struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
298
299 struct sllist queue;
300 sllist_init(&queue);
301
302 io_timer_impl_pop(impl, &queue, task);
303
304 return io_timer_wait_queue_post(&queue, -1, ECANCELED);
305}
306
307static size_t
308io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task)
309{
310 struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
311
312 struct sllist queue;
313 sllist_init(&queue);
314
315 io_timer_impl_pop(impl, &queue, task);
316
317 return ev_task_queue_abort(&queue);
318}
319
320static io_dev_t *
321io_timer_impl_get_dev(const io_timer_t *timer)
322{
323 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
324
325 return &impl->dev_vptr;
326}
327
328static io_clock_t *
329io_timer_impl_get_clock(const io_timer_t *timer)
330{
331 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
332 assert(impl->clockid == CLOCK_REALTIME
333 || impl->clockid == CLOCK_MONOTONIC);
334
335 switch (impl->clockid) {
336 case CLOCK_REALTIME: return IO_CLOCK_REALTIME;
337 case CLOCK_MONOTONIC: return IO_CLOCK_MONOTONIC;
338 default: return NULL;
339 }
340}
341
342static int
343io_timer_impl_getoverrun(const io_timer_t *timer)
344{
345 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
346
347#if !LELY_NO_THREADS
348 pthread_mutex_lock((pthread_mutex_t *)&impl->mtx);
349#endif
350 int overrun = impl->overrun;
351#if !LELY_NO_THREADS
352 pthread_mutex_unlock((pthread_mutex_t *)&impl->mtx);
353#endif
354
355 return overrun;
356}
357
358static int
359io_timer_impl_gettime(const io_timer_t *timer, struct itimerspec *value)
360{
361 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
362
363 return timerfd_gettime(impl->tfd, value);
364}
365
366static int
367io_timer_impl_settime(io_timer_t *timer, int flags,
368 const struct itimerspec *value, struct itimerspec *ovalue)
369{
370 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
371
372 int flags_ = 0;
373 if (flags & TIMER_ABSTIME)
374 flags_ |= TFD_TIMER_ABSTIME;
375
376 return timerfd_settime(impl->tfd, flags_, value, ovalue);
377}
378
379static void
380io_timer_impl_submit_wait(io_timer_t *timer, struct io_timer_wait *wait)
381{
382 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
383 assert(wait);
384 struct ev_task *task = &wait->task;
385
386 if (!task->exec)
387 task->exec = impl->exec;
389
390#if !LELY_NO_THREADS
391 pthread_mutex_lock(&impl->mtx);
392#endif
393 if (impl->shutdown) {
394#if !LELY_NO_THREADS
395 pthread_mutex_unlock(&impl->mtx);
396#endif
397 io_timer_wait_post(wait, -1, ECANCELED);
398 } else {
399 sllist_push_back(&impl->wait_queue, &task->_node);
400#if !LELY_NO_THREADS
401 pthread_mutex_unlock(&impl->mtx);
402#endif
403 }
404}
405
406static int
407io_timer_impl_svc_notify_fork(struct io_svc *svc, enum io_fork_event e)
408{
409 struct io_timer_impl *impl = io_timer_impl_from_svc(svc);
410
411 if (e != IO_FORK_CHILD || impl->shutdown)
412 return 0;
413
414 int result = 0;
415 int errsv = errno;
416
417 struct itimerspec value = { { 0, 0 }, { 0, 0 } };
418 if (timerfd_gettime(impl->tfd, &value) == -1) {
419 errsv = errno;
420 result = -1;
421 }
422
423 if (io_timer_impl_close(impl) == -1 && !result) {
424 errsv = errno;
425 result = -1;
426 }
427
428 if (io_timer_impl_open(impl) == -1 && !result) {
429 errsv = errno;
430 result = -1;
431 }
432
433 if (timerfd_settime(impl->tfd, 0, &value, NULL) == -1 && !result) {
434 errsv = errno;
435 result = -1;
436 }
437
438 errno = errsv;
439 return result;
440}
441
442static void
443io_timer_impl_svc_shutdown(struct io_svc *svc)
444{
445 struct io_timer_impl *impl = io_timer_impl_from_svc(svc);
446 io_dev_t *dev = &impl->dev_vptr;
447
448#if !LELY_NO_THREADS
449 pthread_mutex_lock(&impl->mtx);
450#endif
451 int shutdown = !impl->shutdown;
452 impl->shutdown = 1;
453 if (shutdown) {
454 // Stop monitoring the timer file descriptor.
455 io_poll_watch(impl->poll, impl->tfd, 0, &impl->watch);
456 // Try to abort io_timer_impl_wait_task_func().
457 // clang-format off
458 if (impl->wait_posted && ev_exec_abort(impl->wait_task.exec,
459 &impl->wait_task))
460 // clang-format on
461 impl->wait_posted = 0;
462 }
463#if !LELY_NO_THREADS
464 pthread_mutex_unlock(&impl->mtx);
465#endif
466 // cppcheck-suppress duplicateCondition
467 if (shutdown)
468 // Cancel all pending operations.
469 io_timer_impl_dev_cancel(dev, NULL);
470}
471
472static void
473io_timer_impl_watch_func(struct io_poll_watch *watch, int events)
474{
475 assert(watch);
476 struct io_timer_impl *impl =
477 structof(watch, struct io_timer_impl, watch);
478 (void)events;
479
480#if !LELY_NO_THREADS
481 pthread_mutex_lock(&impl->mtx);
482#endif
483 int post_wait = !impl->wait_posted && !impl->shutdown;
484 if (post_wait)
485 impl->wait_posted = 1;
486#if !LELY_NO_THREADS
487 pthread_mutex_unlock(&impl->mtx);
488#endif
489 // cppcheck-suppress duplicateCondition
490 if (post_wait)
491 ev_exec_post(impl->wait_task.exec, &impl->wait_task);
492}
493
494static void
495io_timer_impl_wait_task_func(struct ev_task *task)
496{
497 assert(task);
498 struct io_timer_impl *impl =
499 structof(task, struct io_timer_impl, wait_task);
500
501 int errsv = errno;
502 int overrun = -1;
503 int events = 0;
504
505 ssize_t result;
506 for (;;) {
507 uintmax_t value = 0;
508 do {
509 errno = 0;
510 result = read(impl->tfd, &value, sizeof(value));
511 } while (result == -1 && errno == EINTR);
512 if (result != sizeof(value))
513 break;
514
515 if (value > (uintmax_t)INT_MAX + 1)
516 value = (uintmax_t)INT_MAX + 1;
517 if (overrun > (int)(INT_MAX - value))
518 overrun = INT_MAX;
519 else
520 overrun += value;
521 }
522 if (result == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
523 errno = 0;
524 events |= IO_EVENT_IN;
525 }
526
527 struct sllist queue;
528 sllist_init(&queue);
529
530#if !LELY_NO_THREADS
531 pthread_mutex_lock(&impl->mtx);
532#endif
533 if (overrun >= 0)
534 impl->overrun = overrun;
535 if (overrun >= 0 || errno)
536 sllist_append(&queue, &impl->wait_queue);
537
538 if (events && !impl->shutdown)
539 io_poll_watch(impl->poll, impl->tfd, events, &impl->watch);
540 impl->wait_posted = 0;
541#if !LELY_NO_THREADS
542 pthread_mutex_unlock(&impl->mtx);
543#endif
544
545 io_timer_wait_queue_post(&queue, overrun, errno);
546 errno = errsv;
547}
548
549static inline struct io_timer_impl *
550io_timer_impl_from_dev(const io_dev_t *dev)
551{
552 assert(dev);
553
554 return structof(dev, struct io_timer_impl, dev_vptr);
555}
556
557static inline struct io_timer_impl *
558io_timer_impl_from_timer(const io_timer_t *timer)
559{
560 assert(timer);
561
562 return structof(timer, struct io_timer_impl, timer_vptr);
563}
564
565static inline struct io_timer_impl *
566io_timer_impl_from_svc(const struct io_svc *svc)
567{
568 assert(svc);
569
570 return structof(svc, struct io_timer_impl, svc);
571}
572
573static void
574io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
575 struct ev_task *task)
576{
577 assert(impl);
578 assert(queue);
579
580#if !LELY_NO_THREADS
581 pthread_mutex_lock(&impl->mtx);
582#endif
583 if (!task)
584 sllist_append(queue, &impl->wait_queue);
585 else if (sllist_remove(&impl->wait_queue, &task->_node))
586 sllist_push_back(queue, &task->_node);
587#if !LELY_NO_THREADS
588 pthread_mutex_unlock(&impl->mtx);
589#endif
590}
591
592static int
593io_timer_impl_open(struct io_timer_impl *impl)
594{
595 assert(impl);
596
597 int errsv = 0;
598
599 if (io_timer_impl_close(impl) == -1) {
600 errsv = errno;
601 goto error_close;
602 }
603
604 impl->tfd = timerfd_create(impl->clockid, TFD_NONBLOCK | TFD_CLOEXEC);
605 if (impl->tfd == -1) {
606 errsv = errno;
607 goto error_timerfd_create;
608 }
609
610 if (io_poll_watch(impl->poll, impl->tfd, IO_EVENT_IN, &impl->watch)
611 == -1) {
612 errsv = errno;
613 goto error_poll_watch;
614 }
615
616 return 0;
617
618error_poll_watch:
619 close(impl->tfd);
620 impl->tfd = -1;
621error_timerfd_create:
622error_close:
623 errno = errsv;
624 return -1;
625}
626
627static int
628io_timer_impl_close(struct io_timer_impl *impl)
629{
630 assert(impl);
631
632 int tfd = impl->tfd;
633 if (tfd == -1)
634 return 0;
635 impl->tfd = -1;
636
637 int result = 0;
638 int errsv = errno;
639
640 if (!impl->shutdown
641 && io_poll_watch(impl->poll, tfd, 0, &impl->watch)
642 == -1) {
643 errsv = errno;
644 result = -1;
645 }
646
647 if (close(tfd) == -1 && !result) {
648 errsv = errno;
649 result = -1;
650 }
651
652 errno = errsv;
653 return result;
654}
655
656#endif // !LELY_NO_STDIO && __linux__
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
io_fork_event
The type of event generated by an I/O context before and after a process fork.
Definition ctx.h:37
@ IO_FORK_CHILD
The event generated after the fork in the child process.
Definition ctx.h:43
#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
@ IO_EVENT_IN
Data (other than priority data) MAY be read without blocking.
Definition event.h:35
size_t ev_exec_abort(ev_exec_t *exec, struct ev_task *task)
Aborts the specified task submitted to *exec, if it has not yet begun executing, or all pending tasks...
Definition exec.h:136
void ev_exec_post(ev_exec_t *exec, struct ev_task *task)
Submits *task to *exec for execution.
Definition exec.h:124
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
const struct io_dev_vtbl *const io_dev_t
An abstract I/O device.
Definition dev.h:35
This header file is part of the I/O library; it contains the I/O polling declarations for POSIX platf...
#define IO_POLL_WATCH_INIT(func)
The static initializer for io_poll_watch.
Definition poll.h:65
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
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
An object representing a file descriptor being monitored for I/O events.
Definition poll.h:56
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
A singly-linked list.
Definition sllist.h:52
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
#define EV_TASK_INIT(exec, func)
The static initializer for ev_task.
Definition task.h:53
int clockid_t
Used for clock ID type in the clock and timer functions.
Definition types.h:40
ptrdiff_t ssize_t
Used for a count of bytes or an error indication.
Definition types.h:43
This header file is part of the C11 and POSIX compatibility library; it includes <unistd....