Lely core libraries  2.2.5
timer.c
Go to the documentation of this file.
1 
24 #include "../timer.h"
25 #if !LELY_NO_THREADS
26 #include <lely/libc/threads.h>
27 #endif
28 #include <lely/io2/ctx.h>
29 #include <lely/io2/user/timer.h>
30 #include <lely/util/errnum.h>
31 #include <lely/util/time.h>
32 #include <lely/util/util.h>
33 
34 #include <assert.h>
35 #include <stdlib.h>
36 
37 static io_ctx_t *io_user_timer_dev_get_ctx(const io_dev_t *dev);
38 static ev_exec_t *io_user_timer_dev_get_exec(const io_dev_t *dev);
39 static size_t io_user_timer_dev_cancel(io_dev_t *dev, struct ev_task *task);
40 static size_t io_user_timer_dev_abort(io_dev_t *dev, struct ev_task *task);
41 
42 // clang-format off
43 static const struct io_dev_vtbl io_user_timer_dev_vtbl = {
44  &io_user_timer_dev_get_ctx,
45  &io_user_timer_dev_get_exec,
46  &io_user_timer_dev_cancel,
47  &io_user_timer_dev_abort
48 };
49 // clang-format on
50 
51 static io_dev_t *io_user_timer_get_dev(const io_timer_t *timer);
52 static io_clock_t *io_user_timer_get_clock(const io_timer_t *timer);
53 static int io_user_timer_getoverrun(const io_timer_t *timer);
54 static int io_user_timer_gettime(
55  const io_timer_t *timer, struct itimerspec *value);
56 static int io_user_timer_settime(io_timer_t *timer, int flags,
57  const struct itimerspec *value, struct itimerspec *ovalue);
58 static void io_user_timer_submit_wait(
59  io_timer_t *timer, struct io_timer_wait *wait);
60 
61 // clang-format off
62 static const struct io_timer_vtbl io_user_timer_vtbl = {
63  &io_user_timer_get_dev,
64  &io_user_timer_get_clock,
65  &io_user_timer_getoverrun,
66  &io_user_timer_gettime,
67  &io_user_timer_settime,
68  &io_user_timer_submit_wait
69 };
70 // clang-format on
71 
72 static int io_user_timer_clock_getres(
73  const io_clock_t *clock, struct timespec *res);
74 static int io_user_timer_clock_gettime(
75  const io_clock_t *clock, struct timespec *tp);
76 static int io_user_timer_clock_settime(
77  io_clock_t *clock, const struct timespec *tp);
78 
79 // clang-format off
80 static const struct io_clock_vtbl io_user_timer_clock_vtbl = {
81  &io_user_timer_clock_getres,
82  &io_user_timer_clock_gettime,
83  &io_user_timer_clock_settime
84 };
85 // clang-format on
86 
87 static void io_user_timer_svc_shutdown(struct io_svc *svc);
88 
89 // clang-format off
90 static const struct io_svc_vtbl io_user_timer_svc_vtbl = {
91  NULL,
92  &io_user_timer_svc_shutdown
93 };
94 // clang-format on
95 
96 struct io_user_timer {
97  const struct io_dev_vtbl *dev_vptr;
98  const struct io_timer_vtbl *timer_vptr;
99  const struct io_clock_vtbl *clock_vptr;
100  struct io_svc svc;
101  io_ctx_t *ctx;
102  ev_exec_t *exec;
104  void *arg;
105 #if !LELY_NO_THREADS
106  mtx_t mtx;
107 #endif
108  int shutdown;
109  struct sllist queue;
110  struct timespec now;
111  struct timespec prev;
112  struct timespec next;
113  struct itimerspec value;
114  int overrun;
115 };
116 
117 static inline struct io_user_timer *io_user_timer_from_dev(const io_dev_t *dev);
118 static inline struct io_user_timer *io_user_timer_from_timer(
119  const io_timer_t *timer);
120 static inline struct io_user_timer *io_user_timer_from_clock(
121  const io_clock_t *clock);
122 static inline struct io_user_timer *io_user_timer_from_svc(
123  const struct io_svc *svc);
124 
125 static void io_user_timer_pop(struct io_user_timer *user, struct sllist *queue,
126  struct ev_task *task);
127 
128 static void io_user_timer_do_settime(struct io_user_timer *user);
129 
130 static inline int timespec_valid(const struct timespec *tp);
131 
132 void *
133 io_user_timer_alloc(void)
134 {
135  struct io_user_timer *user = malloc(sizeof(*user));
136  if (!user)
137  set_errc(errno2c(errno));
138  // cppcheck-suppress memleak symbolName=user
139  return user ? &user->timer_vptr : NULL;
140 }
141 
142 void
143 io_user_timer_free(void *ptr)
144 {
145  if (ptr)
146  free(io_user_timer_from_timer(ptr));
147 }
148 
149 io_timer_t *
150 io_user_timer_init(io_timer_t *timer, io_ctx_t *ctx, ev_exec_t *exec,
151  io_user_timer_setnext_t *func, void *arg)
152 {
153  struct io_user_timer *user = io_user_timer_from_timer(timer);
154  assert(ctx);
155  assert(exec);
156 
157  user->dev_vptr = &io_user_timer_dev_vtbl;
158  user->timer_vptr = &io_user_timer_vtbl;
159  user->clock_vptr = &io_user_timer_clock_vtbl;
160 
161  user->svc = (struct io_svc)IO_SVC_INIT(&io_user_timer_svc_vtbl);
162  user->ctx = ctx;
163 
164  user->exec = exec;
165 
166  user->func = func;
167  user->arg = arg;
168 
169 #if !LELY_NO_THREADS
170  if (mtx_init(&user->mtx, mtx_plain) != thrd_success)
171  return NULL;
172 #endif
173 
174  user->shutdown = 0;
175 
176  sllist_init(&user->queue);
177 
178  user->now = (struct timespec){ 0, 0 };
179  user->prev = (struct timespec){ 0, 0 };
180  user->next = (struct timespec){ 0, 0 };
181  user->value = (struct itimerspec){ { 0, 0 }, { 0, 0 } };
182  user->overrun = 0;
183 
184  io_ctx_insert(user->ctx, &user->svc);
185 
186  return timer;
187 }
188 
189 void
190 io_user_timer_fini(io_timer_t *timer)
191 {
192  struct io_user_timer *user = io_user_timer_from_timer(timer);
193 
194  io_ctx_remove(user->ctx, &user->svc);
195  // Cancel all pending tasks.
196  io_user_timer_svc_shutdown(&user->svc);
197 
198 #if !LELY_NO_THREADS
199  mtx_destroy(&user->mtx);
200 #endif
201 }
202 
203 io_timer_t *
205  io_user_timer_setnext_t *func, void *arg)
206 {
207  int errc = 0;
208 
209  io_timer_t *timer = io_user_timer_alloc();
210  if (!timer) {
211  errc = get_errc();
212  goto error_alloc;
213  }
214 
215  io_timer_t *tmp = io_user_timer_init(timer, ctx, exec, func, arg);
216  if (!tmp) {
217  errc = get_errc();
218  goto error_init;
219  }
220  timer = tmp;
221 
222  return timer;
223 
224 error_init:
225  io_user_timer_free((void *)timer);
226 error_alloc:
227  set_errc(errc);
228  return NULL;
229 }
230 
231 void
233 {
234  if (timer) {
235  io_user_timer_fini(timer);
236  io_user_timer_free((void *)timer);
237  }
238 }
239 
240 static io_ctx_t *
241 io_user_timer_dev_get_ctx(const io_dev_t *dev)
242 {
243  const struct io_user_timer *user = io_user_timer_from_dev(dev);
244 
245  return user->ctx;
246 }
247 
248 static ev_exec_t *
249 io_user_timer_dev_get_exec(const io_dev_t *dev)
250 {
251  const struct io_user_timer *user = io_user_timer_from_dev(dev);
252 
253  return user->exec;
254 }
255 
256 static size_t
257 io_user_timer_dev_cancel(io_dev_t *dev, struct ev_task *task)
258 {
259  struct io_user_timer *user = io_user_timer_from_dev(dev);
260 
261  struct sllist queue;
262  sllist_init(&queue);
263 
264  io_user_timer_pop(user, &queue, task);
265 
266  return io_timer_wait_queue_post(&queue, -1, errnum2c(ERRNUM_CANCELED));
267 }
268 
269 static size_t
270 io_user_timer_dev_abort(io_dev_t *dev, struct ev_task *task)
271 {
272  struct io_user_timer *user = io_user_timer_from_dev(dev);
273 
274  struct sllist queue;
275  sllist_init(&queue);
276 
277  io_user_timer_pop(user, &queue, task);
278 
279  return ev_task_queue_abort(&queue);
280 }
281 
282 static io_dev_t *
283 io_user_timer_get_dev(const io_timer_t *timer)
284 {
285  const struct io_user_timer *user = io_user_timer_from_timer(timer);
286 
287  return &user->dev_vptr;
288 }
289 
290 static io_clock_t *
291 io_user_timer_get_clock(const io_timer_t *timer)
292 {
293  const struct io_user_timer *user = io_user_timer_from_timer(timer);
294 
295  return &user->clock_vptr;
296 }
297 
298 static int
299 io_user_timer_getoverrun(const io_timer_t *timer)
300 {
301  const struct io_user_timer *user = io_user_timer_from_timer(timer);
302 
303 #if !LELY_NO_THREADS
304  mtx_lock((mtx_t *)&user->mtx);
305 #endif
306  int overrun = user->overrun;
307 #if !LELY_NO_THREADS
308  mtx_unlock((mtx_t *)&user->mtx);
309 #endif
310  return overrun;
311 }
312 
313 static int
314 io_user_timer_gettime(const io_timer_t *timer, struct itimerspec *value)
315 {
316  const struct io_user_timer *user = io_user_timer_from_timer(timer);
317 
318  if (value) {
319 #if !LELY_NO_THREADS
320  mtx_lock((mtx_t *)&user->mtx);
321 #endif
322  *value = user->value;
323  if (value->it_value.tv_sec || value->it_value.tv_nsec)
324  timespec_sub(&value->it_value, &user->now);
325 #if !LELY_NO_THREADS
326  mtx_unlock((mtx_t *)&user->mtx);
327 #endif
328  }
329  return 0;
330 }
331 
332 static int
333 io_user_timer_settime(io_timer_t *timer, int flags,
334  const struct itimerspec *value, struct itimerspec *ovalue)
335 {
336  struct io_user_timer *user = io_user_timer_from_timer(timer);
337  assert(value);
338 
339  struct timespec expiry = value->it_value;
340  if (!timespec_valid(&value->it_interval) || !timespec_valid(&expiry)) {
342  return -1;
343  }
344 
345 #if !LELY_NO_THREADS
346  // NOTE: mtx_unlock() is called by io_user_timer_do_settime().
347  mtx_lock(&user->mtx);
348 #endif
349 
350  if ((expiry.tv_sec || expiry.tv_nsec) && !(flags & TIMER_ABSTIME))
351  timespec_add(&expiry, &user->now);
352 
353  if (ovalue) {
354  *ovalue = user->value;
355  if (ovalue->it_value.tv_sec || ovalue->it_value.tv_nsec)
356  timespec_sub(&ovalue->it_value, &user->now);
357  }
358  user->value = (struct itimerspec){ value->it_interval, expiry };
359  user->overrun = 0;
360 
361  io_user_timer_do_settime(user);
362  return 0;
363 }
364 
365 static void
366 io_user_timer_submit_wait(io_timer_t *timer, struct io_timer_wait *wait)
367 {
368  struct io_user_timer *user = io_user_timer_from_timer(timer);
369  assert(wait);
370  struct ev_task *task = &wait->task;
371 
372  if (!task->exec)
373  task->exec = user->exec;
374  ev_exec_on_task_init(task->exec);
375 
376 #if !LELY_NO_THREADS
377  mtx_lock(&user->mtx);
378 #endif
379  if (user->shutdown) {
380 #if !LELY_NO_THREADS
381  mtx_unlock(&user->mtx);
382 #endif
383  io_timer_wait_post(wait, -1, errnum2c(ERRNUM_CANCELED));
384  } else {
385  sllist_push_back(&user->queue, &task->_node);
386  // NOTE: mtx_unlock() is called by io_user_timer_do_settime().
387  io_user_timer_do_settime(user);
388  }
389 }
390 
391 static int
392 io_user_timer_clock_getres(const io_clock_t *clock, struct timespec *res)
393 {
394  const struct io_user_timer *user = io_user_timer_from_clock(clock);
395 
396  if (res) {
397 #if !LELY_NO_THREADS
398  mtx_lock((mtx_t *)&user->mtx);
399 #endif
400  *res = user->now;
401  timespec_sub(res, &user->prev);
402 #if !LELY_NO_THREADS
403  mtx_unlock((mtx_t *)&user->mtx);
404 #endif
405  }
406  return 0;
407 }
408 
409 static int
410 io_user_timer_clock_gettime(const io_clock_t *clock, struct timespec *tp)
411 {
412  const struct io_user_timer *user = io_user_timer_from_clock(clock);
413 
414  if (tp) {
415 #if !LELY_NO_THREADS
416  mtx_lock((mtx_t *)&user->mtx);
417 #endif
418  *tp = user->now;
419 #if !LELY_NO_THREADS
420  mtx_unlock((mtx_t *)&user->mtx);
421 #endif
422  }
423  return 0;
424 }
425 
426 static int
427 io_user_timer_clock_settime(io_clock_t *clock, const struct timespec *tp)
428 {
429  struct io_user_timer *user = io_user_timer_from_clock(clock);
430  assert(tp);
431 
432 #if !LELY_NO_THREADS
433  // NOTE: mtx_unlock() is called by io_user_timer_do_settime().
434  mtx_lock(&user->mtx);
435 #endif
436 
437  user->prev = user->now;
438  user->now = *tp;
439 
440  io_user_timer_do_settime(user);
441  return 0;
442 }
443 
444 static void
445 io_user_timer_svc_shutdown(struct io_svc *svc)
446 {
447  struct io_user_timer *user = io_user_timer_from_svc(svc);
448  io_dev_t *dev = &user->dev_vptr;
449 
450 #if !LELY_NO_THREADS
451  mtx_lock(&user->mtx);
452 #endif
453  int shutdown = !user->shutdown;
454  user->shutdown = 1;
455 #if !LELY_NO_THREADS
456  mtx_unlock(&user->mtx);
457 #endif
458 
459  if (shutdown)
460  // Cancel all pending tasks.
461  io_user_timer_dev_cancel(dev, NULL);
462 }
463 
464 static inline struct io_user_timer *
465 io_user_timer_from_dev(const io_dev_t *dev)
466 {
467  assert(dev);
468 
469  return structof(dev, struct io_user_timer, dev_vptr);
470 }
471 
472 static inline struct io_user_timer *
473 io_user_timer_from_timer(const io_timer_t *timer)
474 {
475  assert(timer);
476 
477  return structof(timer, struct io_user_timer, timer_vptr);
478 }
479 
480 static inline struct io_user_timer *
481 io_user_timer_from_clock(const io_clock_t *clock)
482 {
483  assert(clock);
484 
485  return structof(clock, struct io_user_timer, clock_vptr);
486 }
487 
488 static inline struct io_user_timer *
489 io_user_timer_from_svc(const struct io_svc *svc)
490 {
491  assert(svc);
492 
493  return structof(svc, struct io_user_timer, svc);
494 }
495 
496 static void
497 io_user_timer_pop(struct io_user_timer *user, struct sllist *queue,
498  struct ev_task *task)
499 {
500  assert(user);
501  assert(queue);
502 
503 #if !LELY_NO_THREADS
504  mtx_lock(&user->mtx);
505 #endif
506  if (!task)
507  sllist_append(queue, &user->queue);
508  else if (sllist_remove(&user->queue, &task->_node))
509  sllist_push_back(queue, &task->_node);
510 #if !LELY_NO_THREADS
511  mtx_unlock(&user->mtx);
512 #endif
513 }
514 
515 static void
516 io_user_timer_do_settime(struct io_user_timer *user)
517 {
518  assert(user);
519 
520  struct sllist queue;
521  sllist_init(&queue);
522 
523  struct timespec *expiry = &user->value.it_value;
524  assert(timespec_valid(expiry));
525 
526  if ((expiry->tv_sec || expiry->tv_nsec)
527  && timespec_cmp(expiry, &user->now) <= 0) {
528  const struct timespec *period = &user->value.it_interval;
529  assert(timespec_valid(period));
530 
531  if (period->tv_sec || period->tv_nsec) {
532  user->overrun = -1;
533  while (timespec_cmp(expiry, &user->now) < 0) {
534  timespec_add(expiry, period);
535  user->overrun++;
536  }
537  } else {
538  *expiry = (struct timespec){ 0, 0 };
539  user->overrun = 0;
540  }
541 
542  sllist_append(&queue, &user->queue);
543  }
544 
545  struct timespec next = { 0, 0 };
546  io_user_timer_setnext_t *func = NULL;
547  void *arg = NULL;
548  if (timespec_cmp(&user->next, expiry)) {
549  next = user->next = *expiry;
550  func = user->func;
551  arg = user->arg;
552  }
553 
554  int overrun = user->overrun;
555 
556 #if !LELY_NO_THREADS
557  mtx_unlock(&user->mtx);
558 #endif
559 
560  if (func)
561  func(&next, arg);
562 
563  io_timer_wait_queue_post(&queue, overrun, 0);
564 }
565 
566 static inline int
567 timespec_valid(const struct timespec *tp)
568 {
569  return tp->tv_sec >= 0 && tp->tv_nsec >= 0 && tp->tv_nsec < 1000000000l;
570 }
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:121
#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:136
This header file is part of the utilities library; it contains the native and platform-independent er...
int errnum2c(errnum_t errnum)
Transforms a platform-independent error number to a native error code.
Definition: errnum.c:825
@ ERRNUM_INVAL
Invalid argument.
Definition: errnum.h:129
@ ERRNUM_CANCELED
Operation canceled.
Definition: errnum.h:96
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:947
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:43
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
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:108
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition: ev.h:29
const struct io_timer_vtbl *const io_timer_t
An abstract timer.
Definition: timer.h:38
This header file is part of the I/O library; it contains the user-defined timer declarations.
void io_user_timer_setnext_t(const struct timespec *tp, void *arg)
The type of function invoked by a user-defined timer when the expiration time is updated with io_time...
Definition: timer.h:45
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
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
Definition: sllist.h:184
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:233
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:213
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
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:35
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 singly-linked list.
Definition: sllist.h:51
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
This header file is part of the C11 and POSIX compatibility library; it includes <threads....
int mtx_init(mtx_t *mtx, int type)
Creates a mutex object with properties indicated by type, which must have one of the four values:
int mtx_lock(mtx_t *mtx)
Blocks until it locks the mutex at mtx.
@ thrd_success
Indicates that the requested operation succeeded.
Definition: threads.h:121
int mtx_unlock(mtx_t *mtx)
Unlocks the mutex at mtx.
pthread_mutex_t mtx_t
A complete object type that holds an identifier for a mutex.
Definition: threads.h:102
void mtx_destroy(mtx_t *mtx)
Releases any resources used by the mutex at mtx.
@ mtx_plain
A mutex type that supports neither timeout nor test and return.
Definition: threads.h:109
void io_user_timer_destroy(io_timer_t *timer)
Destroys a user-defined timer.
Definition: timer.c:232
io_timer_t * io_user_timer_create(io_ctx_t *ctx, ev_exec_t *exec, io_user_timer_setnext_t *func, void *arg)
Creates a new user-defined timer.
Definition: timer.c:204
This header file is part of the utilities library; it contains the time function declarations.
int timespec_cmp(const void *p1, const void *p2)
Compares two times.
Definition: time.h:228
void timespec_add(struct timespec *tp, const struct timespec *inc)
Adds the time interval *inc to the time at tp.
Definition: time.h:117
void timespec_sub(struct timespec *tp, const struct timespec *dec)
Subtracts the time interval *dec from the time at tp.
Definition: time.h:159