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 
44 static io_ctx_t *io_timer_impl_dev_get_ctx(const io_dev_t *dev);
45 static ev_exec_t *io_timer_impl_dev_get_exec(const io_dev_t *dev);
46 static size_t io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task);
47 static size_t io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task);
48 
49 // clang-format off
50 static 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 
58 static io_dev_t *io_timer_impl_get_dev(const io_timer_t *timer);
59 static io_clock_t *io_timer_impl_get_clock(const io_timer_t *timer);
60 static int io_timer_impl_getoverrun(const io_timer_t *timer);
61 static int io_timer_impl_gettime(
62  const io_timer_t *timer, struct itimerspec *value);
63 static int io_timer_impl_settime(io_timer_t *timer, int flags,
64  const struct itimerspec *value, struct itimerspec *ovalue);
65 static void io_timer_impl_submit_wait(
66  io_timer_t *timer, struct io_timer_wait *wait);
67 
68 // clang-format off
69 static 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 
79 static void io_timer_impl_svc_shutdown(struct io_svc *svc);
80 
81 // clang-format off
82 static const struct io_svc_vtbl io_timer_impl_svc_vtbl = {
83  NULL,
84  &io_timer_impl_svc_shutdown
85 };
86 // clang-format on
87 
88 struct 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 
103 static void io_timer_impl_notify_function(union sigval val);
104 
105 static inline struct io_timer_impl *io_timer_impl_from_dev(const io_dev_t *dev);
106 static inline struct io_timer_impl *io_timer_impl_from_timer(
107  const io_timer_t *timer);
108 static inline struct io_timer_impl *io_timer_impl_from_svc(
109  const struct io_svc *svc);
110 
111 static void io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
112  struct ev_task *task);
113 
114 void *
115 io_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 
126 void
127 io_timer_free(void *ptr)
128 {
129  if (ptr)
130  free(io_timer_impl_from_timer(ptr));
131 }
132 
133 io_timer_t *
134 io_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);
181 error_init_mtx:
182 #endif
183  timer_delete(impl->timerid);
184 error_create_timer:
185  errno = errsv;
186  return NULL;
187 }
188 
189 void
190 io_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 
212 io_timer_t *
213 io_timer_create(io_poll_t *poll, ev_exec_t *exec, clockid_t clockid)
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 
232 error_init:
233  io_timer_free((void *)timer);
234 error_alloc:
235  errno = errsv;
236  return NULL;
237 }
238 
239 void
241 {
242  if (timer) {
243  io_timer_fini(timer);
244  io_timer_free((void *)timer);
245  }
246 }
247 
248 static io_ctx_t *
249 io_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 
256 static ev_exec_t *
257 io_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 
264 static size_t
265 io_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 
277 static size_t
278 io_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 
290 static io_dev_t *
291 io_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 
298 static io_clock_t *
299 io_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 
312 static int
313 io_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 
320 static int
321 io_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 
328 static int
329 io_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 
337 static void
338 io_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;
346  ev_exec_on_task_init(task->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 
364 static void
365 io_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 
384 static void
385 io_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 
409 static inline struct io_timer_impl *
410 io_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 
417 static inline struct io_timer_impl *
418 io_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 
425 static inline struct io_timer_impl *
426 io_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 
433 static void
434 io_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 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