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 && _POSIX_C_SOURCE >= 199309L
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 <stdlib.h>
38
39#if !LELY_NO_THREADS
40#include <pthread.h>
41#endif
42#include <signal.h>
43
44static io_ctx_t *io_timer_impl_dev_get_ctx(const io_dev_t *dev);
45static ev_exec_t *io_timer_impl_dev_get_exec(const io_dev_t *dev);
46static size_t io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task);
47static size_t io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task);
48
49// clang-format off
50static const struct io_dev_vtbl io_timer_impl_dev_vtbl = {
51 &io_timer_impl_dev_get_ctx,
52 &io_timer_impl_dev_get_exec,
53 &io_timer_impl_dev_cancel,
54 &io_timer_impl_dev_abort
55};
56// clang-format on
57
58static io_dev_t *io_timer_impl_get_dev(const io_timer_t *timer);
59static io_clock_t *io_timer_impl_get_clock(const io_timer_t *timer);
60static int io_timer_impl_getoverrun(const io_timer_t *timer);
61static int io_timer_impl_gettime(
62 const io_timer_t *timer, struct itimerspec *value);
63static int io_timer_impl_settime(io_timer_t *timer, int flags,
64 const struct itimerspec *value, struct itimerspec *ovalue);
65static void io_timer_impl_submit_wait(
66 io_timer_t *timer, struct io_timer_wait *wait);
67
68// clang-format off
69static const struct io_timer_vtbl io_timer_impl_vtbl = {
70 &io_timer_impl_get_dev,
71 &io_timer_impl_get_clock,
72 &io_timer_impl_getoverrun,
73 &io_timer_impl_gettime,
74 &io_timer_impl_settime,
75 &io_timer_impl_submit_wait
76};
77// clang-format on
78
79static void io_timer_impl_svc_shutdown(struct io_svc *svc);
80
81// clang-format off
82static const struct io_svc_vtbl io_timer_impl_svc_vtbl = {
83 NULL,
84 &io_timer_impl_svc_shutdown
85};
86// clang-format on
87
88struct io_timer_impl {
89 const struct io_dev_vtbl *dev_vptr;
90 const struct io_timer_vtbl *timer_vptr;
91 struct io_svc svc;
92 io_ctx_t *ctx;
93 ev_exec_t *exec;
94 clockid_t clockid;
95 timer_t timerid;
96#if !LELY_NO_THREADS
97 pthread_mutex_t mtx;
98#endif
99 int shutdown;
100 struct sllist wait_queue;
101};
102
103static void io_timer_impl_notify_function(union sigval val);
104
105static inline struct io_timer_impl *io_timer_impl_from_dev(const io_dev_t *dev);
106static inline struct io_timer_impl *io_timer_impl_from_timer(
107 const io_timer_t *timer);
108static inline struct io_timer_impl *io_timer_impl_from_svc(
109 const struct io_svc *svc);
110
111static void io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
112 struct ev_task *task);
113
114void *
115io_timer_alloc(void)
116{
117 struct io_timer_impl *impl = malloc(sizeof(*impl));
118 if (!impl)
119 return NULL;
120 // Suppress a GCC maybe-uninitialized warning.
121 impl->timer_vptr = NULL;
122 // cppcheck-suppress memleak symbolName=impl
123 return &impl->timer_vptr;
124}
125
126void
127io_timer_free(void *ptr)
128{
129 if (ptr)
130 free(io_timer_impl_from_timer(ptr));
131}
132
134io_timer_init(io_timer_t *timer, io_poll_t *poll, ev_exec_t *exec,
135 clockid_t clockid)
136{
137 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
138 assert(poll);
139 assert(exec);
140 io_ctx_t *ctx = io_poll_get_ctx(poll);
141 assert(ctx);
142
143 int errsv = 0;
144
145 impl->dev_vptr = &io_timer_impl_dev_vtbl;
146 impl->timer_vptr = &io_timer_impl_vtbl;
147
148 impl->svc = (struct io_svc)IO_SVC_INIT(&io_timer_impl_svc_vtbl);
149 impl->ctx = ctx;
150
151 impl->exec = exec;
152
153 impl->clockid = clockid;
154
155 struct sigevent ev;
156 ev.sigev_notify = SIGEV_THREAD;
157 ev.sigev_signo = 0;
158 ev.sigev_value.sival_ptr = impl;
159 ev.sigev_notify_function = &io_timer_impl_notify_function;
160 ev.sigev_notify_attributes = NULL;
161 if (timer_create(clockid, &ev, &impl->timerid) == -1) {
162 errsv = errno;
163 goto error_create_timer;
164 }
165
166#if !LELY_NO_THREADS
167 if ((errsv = pthread_mutex_init(&impl->mtx, NULL)))
168 goto error_init_mtx;
169#endif
170
171 impl->shutdown = 0;
172
173 sllist_init(&impl->wait_queue);
174
175 io_ctx_insert(impl->ctx, &impl->svc);
176
177 return timer;
178
179#if !LELY_NO_THREADS
180 // pthread_mutex_destroy(&impl->mtx);
181error_init_mtx:
182#endif
183 timer_delete(impl->timerid);
184error_create_timer:
185 errno = errsv;
186 return NULL;
187}
188
189void
190io_timer_fini(io_timer_t *timer)
191{
192 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
193
194 io_ctx_remove(impl->ctx, &impl->svc);
195 // Cancel all pending tasks.
196 io_timer_impl_svc_shutdown(&impl->svc);
197
198 // Disarm the timer.
199 struct itimerspec value = { { 0, 0 }, { 0, 0 } };
200 timer_settime(impl->timerid, 0, &value, NULL);
201
202 // TODO: Find a reliable way to wait for io_timer_impl_notify_function()
203 // to complete.
204
205#if !LELY_NO_THREADS
206 pthread_mutex_destroy(&impl->mtx);
207#endif
208
209 timer_delete(impl->timerid);
210}
211
214{
215 int errsv = 0;
216
217 io_timer_t *timer = io_timer_alloc();
218 if (!timer) {
219 errsv = errno;
220 goto error_alloc;
221 }
222
223 io_timer_t *tmp = io_timer_init(timer, poll, exec, clockid);
224 if (!tmp) {
225 errsv = errno;
226 goto error_init;
227 }
228 timer = tmp;
229
230 return timer;
231
232error_init:
233 io_timer_free((void *)timer);
234error_alloc:
235 errno = errsv;
236 return NULL;
237}
238
239void
241{
242 if (timer) {
243 io_timer_fini(timer);
244 io_timer_free((void *)timer);
245 }
246}
247
248static io_ctx_t *
249io_timer_impl_dev_get_ctx(const io_dev_t *dev)
250{
251 const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
252
253 return impl->ctx;
254}
255
256static ev_exec_t *
257io_timer_impl_dev_get_exec(const io_dev_t *dev)
258{
259 const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
260
261 return impl->exec;
262}
263
264static size_t
265io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task)
266{
267 struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
268
269 struct sllist queue;
270 sllist_init(&queue);
271
272 io_timer_impl_pop(impl, &queue, task);
273
274 return io_timer_wait_queue_post(&queue, -1, ECANCELED);
275}
276
277static size_t
278io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task)
279{
280 struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
281
282 struct sllist queue;
283 sllist_init(&queue);
284
285 io_timer_impl_pop(impl, &queue, task);
286
287 return ev_task_queue_abort(&queue);
288}
289
290static io_dev_t *
291io_timer_impl_get_dev(const io_timer_t *timer)
292{
293 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
294
295 return &impl->dev_vptr;
296}
297
298static io_clock_t *
299io_timer_impl_get_clock(const io_timer_t *timer)
300{
301 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
302 assert(impl->clockid == CLOCK_REALTIME
303 || impl->clockid == CLOCK_MONOTONIC);
304
305 switch (impl->clockid) {
306 case CLOCK_REALTIME: return IO_CLOCK_REALTIME;
307 case CLOCK_MONOTONIC: return IO_CLOCK_MONOTONIC;
308 default: return NULL;
309 }
310}
311
312static int
313io_timer_impl_getoverrun(const io_timer_t *timer)
314{
315 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
316
317 return timer_getoverrun(impl->timerid);
318}
319
320static int
321io_timer_impl_gettime(const io_timer_t *timer, struct itimerspec *value)
322{
323 const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
324
325 return timer_gettime(impl->timerid, value);
326}
327
328static int
329io_timer_impl_settime(io_timer_t *timer, int flags,
330 const struct itimerspec *value, struct itimerspec *ovalue)
331{
332 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
333
334 return timer_settime(impl->timerid, flags, value, ovalue);
335}
336
337static void
338io_timer_impl_submit_wait(io_timer_t *timer, struct io_timer_wait *wait)
339{
340 struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
341 assert(wait);
342 struct ev_task *task = &wait->task;
343
344 if (!task->exec)
345 task->exec = impl->exec;
347
348#if !LELY_NO_THREADS
349 pthread_mutex_lock(&impl->mtx);
350#endif
351 if (impl->shutdown) {
352#if !LELY_NO_THREADS
353 pthread_mutex_unlock(&impl->mtx);
354#endif
355 io_timer_wait_post(wait, -1, ECANCELED);
356 } else {
357 sllist_push_back(&impl->wait_queue, &task->_node);
358#if !LELY_NO_THREADS
359 pthread_mutex_unlock(&impl->mtx);
360#endif
361 }
362}
363
364static void
365io_timer_impl_svc_shutdown(struct io_svc *svc)
366{
367 struct io_timer_impl *impl = io_timer_impl_from_svc(svc);
368 io_dev_t *dev = &impl->dev_vptr;
369
370#if !LELY_NO_THREADS
371 pthread_mutex_lock(&impl->mtx);
372#endif
373 int shutdown = !impl->shutdown;
374 impl->shutdown = 1;
375#if !LELY_NO_THREADS
376 pthread_mutex_unlock(&impl->mtx);
377#endif
378
379 if (shutdown)
380 // Cancel all pending operations.
381 io_timer_impl_dev_cancel(dev, NULL);
382}
383
384static void
385io_timer_impl_notify_function(union sigval val)
386{
387 struct io_timer_impl *impl = val.sival_ptr;
388 assert(impl);
389
390 int errsv = errno;
391 errno = 0;
392 int overrun = timer_getoverrun(impl->timerid);
393
394 struct sllist queue;
395 sllist_init(&queue);
396
397#if !LELY_NO_THREADS
398 pthread_mutex_lock(&impl->mtx);
399#endif
400 sllist_append(&queue, &impl->wait_queue);
401#if !LELY_NO_THREADS
402 pthread_mutex_unlock(&impl->mtx);
403#endif
404
405 io_timer_wait_queue_post(&queue, overrun, errno);
406 errno = errsv;
407}
408
409static inline struct io_timer_impl *
410io_timer_impl_from_dev(const io_dev_t *dev)
411{
412 assert(dev);
413
414 return structof(dev, struct io_timer_impl, dev_vptr);
415}
416
417static inline struct io_timer_impl *
418io_timer_impl_from_timer(const io_timer_t *timer)
419{
420 assert(timer);
421
422 return structof(timer, struct io_timer_impl, timer_vptr);
423}
424
425static inline struct io_timer_impl *
426io_timer_impl_from_svc(const struct io_svc *svc)
427{
428 assert(svc);
429
430 return structof(svc, struct io_timer_impl, svc);
431}
432
433static void
434io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
435 struct ev_task *task)
436{
437 assert(impl);
438 assert(queue);
439
440#if !LELY_NO_THREADS
441 pthread_mutex_lock(&impl->mtx);
442#endif
443 if (!task)
444 sllist_append(queue, &impl->wait_queue);
445 else if (sllist_remove(&impl->wait_queue, &task->_node))
446 sllist_push_back(queue, &task->_node);
447#if !LELY_NO_THREADS
448 pthread_mutex_unlock(&impl->mtx);
449#endif
450}
451
452#endif // !LELY_NO_STDIO && _POSIX_C_SOURCE >= 199309L
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
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...
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
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
int clockid_t
Used for clock ID type in the clock and timer functions.
Definition types.h:40