Lely core libraries  2.2.5
timer.c
Go to the documentation of this file.
1 
24 #include "io.h"
25 
26 #if _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  // cppcheck-suppress memleak symbolName=impl
119  return impl ? &impl->timer_vptr : NULL;
120 }
121 
122 void
123 io_timer_free(void *ptr)
124 {
125  if (ptr)
126  free(io_timer_impl_from_timer(ptr));
127 }
128 
129 io_timer_t *
130 io_timer_init(io_timer_t *timer, io_poll_t *poll, ev_exec_t *exec,
131  clockid_t clockid)
132 {
133  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
134  assert(poll);
135  assert(exec);
136  io_ctx_t *ctx = io_poll_get_ctx(poll);
137  assert(ctx);
138 
139  int errsv = 0;
140 
141  impl->dev_vptr = &io_timer_impl_dev_vtbl;
142  impl->timer_vptr = &io_timer_impl_vtbl;
143 
144  impl->svc = (struct io_svc)IO_SVC_INIT(&io_timer_impl_svc_vtbl);
145  impl->ctx = ctx;
146 
147  impl->exec = exec;
148 
149  impl->clockid = clockid;
150 
151  struct sigevent ev;
152  ev.sigev_notify = SIGEV_THREAD;
153  ev.sigev_signo = 0;
154  ev.sigev_value.sival_ptr = impl;
155  ev.sigev_notify_function = &io_timer_impl_notify_function;
156  ev.sigev_notify_attributes = NULL;
157  if (timer_create(clockid, &ev, &impl->timerid) == -1) {
158  errsv = errno;
159  goto error_create_timer;
160  }
161 
162 #if !LELY_NO_THREADS
163  if ((errsv = pthread_mutex_init(&impl->mtx, NULL)))
164  goto error_init_mtx;
165 #endif
166 
167  impl->shutdown = 0;
168 
169  sllist_init(&impl->wait_queue);
170 
171  io_ctx_insert(impl->ctx, &impl->svc);
172 
173  return timer;
174 
175 #if !LELY_NO_THREADS
176  // pthread_mutex_destroy(&impl->mtx);
177 error_init_mtx:
178 #endif
179  timer_delete(impl->timerid);
180 error_create_timer:
181  errno = errsv;
182  return NULL;
183 }
184 
185 void
186 io_timer_fini(io_timer_t *timer)
187 {
188  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
189 
190  io_ctx_remove(impl->ctx, &impl->svc);
191  // Cancel all pending tasks.
192  io_timer_impl_svc_shutdown(&impl->svc);
193 
194  // Disarm the timer.
195  struct itimerspec value = { { 0, 0 }, { 0, 0 } };
196  timer_settime(impl->timerid, 0, &value, NULL);
197 
198  // TODO: Find a reliable way to wait for io_timer_impl_notify_function()
199  // to complete.
200 
201 #if !LELY_NO_THREADS
202  pthread_mutex_destroy(&impl->mtx);
203 #endif
204 
205  timer_delete(impl->timerid);
206 }
207 
208 io_timer_t *
209 io_timer_create(io_poll_t *poll, ev_exec_t *exec, clockid_t clockid)
210 {
211  int errsv = 0;
212 
213  io_timer_t *timer = io_timer_alloc();
214  if (!timer) {
215  errsv = errno;
216  goto error_alloc;
217  }
218 
219  io_timer_t *tmp = io_timer_init(timer, poll, exec, clockid);
220  if (!tmp) {
221  errsv = errno;
222  goto error_init;
223  }
224  timer = tmp;
225 
226  return timer;
227 
228 error_init:
229  io_timer_free((void *)timer);
230 error_alloc:
231  errno = errsv;
232  return NULL;
233 }
234 
235 void
237 {
238  if (timer) {
239  io_timer_fini(timer);
240  io_timer_free((void *)timer);
241  }
242 }
243 
244 static io_ctx_t *
245 io_timer_impl_dev_get_ctx(const io_dev_t *dev)
246 {
247  const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
248 
249  return impl->ctx;
250 }
251 
252 static ev_exec_t *
253 io_timer_impl_dev_get_exec(const io_dev_t *dev)
254 {
255  const struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
256 
257  return impl->exec;
258 }
259 
260 static size_t
261 io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task)
262 {
263  struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
264 
265  struct sllist queue;
266  sllist_init(&queue);
267 
268  io_timer_impl_pop(impl, &queue, task);
269 
270  return io_timer_wait_queue_post(&queue, -1, ECANCELED);
271 }
272 
273 static size_t
274 io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task)
275 {
276  struct io_timer_impl *impl = io_timer_impl_from_dev(dev);
277 
278  struct sllist queue;
279  sllist_init(&queue);
280 
281  io_timer_impl_pop(impl, &queue, task);
282 
283  return ev_task_queue_abort(&queue);
284 }
285 
286 static io_dev_t *
287 io_timer_impl_get_dev(const io_timer_t *timer)
288 {
289  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
290 
291  return &impl->dev_vptr;
292 }
293 
294 static io_clock_t *
295 io_timer_impl_get_clock(const io_timer_t *timer)
296 {
297  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
298  assert(impl->clockid == CLOCK_REALTIME
299  || impl->clockid == CLOCK_MONOTONIC);
300 
301  switch (impl->clockid) {
302  case CLOCK_REALTIME: return IO_CLOCK_REALTIME;
303  case CLOCK_MONOTONIC: return IO_CLOCK_MONOTONIC;
304  default: return NULL;
305  }
306 }
307 
308 static int
309 io_timer_impl_getoverrun(const io_timer_t *timer)
310 {
311  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
312 
313  return timer_getoverrun(impl->timerid);
314 }
315 
316 static int
317 io_timer_impl_gettime(const io_timer_t *timer, struct itimerspec *value)
318 {
319  const struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
320 
321  return timer_gettime(impl->timerid, value);
322 }
323 
324 static int
325 io_timer_impl_settime(io_timer_t *timer, int flags,
326  const struct itimerspec *value, struct itimerspec *ovalue)
327 {
328  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
329 
330  return timer_settime(impl->timerid, flags, value, ovalue);
331 }
332 
333 static void
334 io_timer_impl_submit_wait(io_timer_t *timer, struct io_timer_wait *wait)
335 {
336  struct io_timer_impl *impl = io_timer_impl_from_timer(timer);
337  assert(wait);
338  struct ev_task *task = &wait->task;
339 
340  if (!task->exec)
341  task->exec = impl->exec;
342  ev_exec_on_task_init(task->exec);
343 
344 #if !LELY_NO_THREADS
345  pthread_mutex_lock(&impl->mtx);
346 #endif
347  if (impl->shutdown) {
348 #if !LELY_NO_THREADS
349  pthread_mutex_unlock(&impl->mtx);
350 #endif
351  io_timer_wait_post(wait, -1, ECANCELED);
352  } else {
353  sllist_push_back(&impl->wait_queue, &task->_node);
354 #if !LELY_NO_THREADS
355  pthread_mutex_unlock(&impl->mtx);
356 #endif
357  }
358 }
359 
360 static void
361 io_timer_impl_svc_shutdown(struct io_svc *svc)
362 {
363  struct io_timer_impl *impl = io_timer_impl_from_svc(svc);
364  io_dev_t *dev = &impl->dev_vptr;
365 
366 #if !LELY_NO_THREADS
367  pthread_mutex_lock(&impl->mtx);
368 #endif
369  int shutdown = !impl->shutdown;
370  impl->shutdown = 1;
371 #if !LELY_NO_THREADS
372  pthread_mutex_unlock(&impl->mtx);
373 #endif
374 
375  if (shutdown)
376  // Cancel all pending operations.
377  io_timer_impl_dev_cancel(dev, NULL);
378 }
379 
380 static void
381 io_timer_impl_notify_function(union sigval val)
382 {
383  struct io_timer_impl *impl = val.sival_ptr;
384  assert(impl);
385 
386  int errsv = errno;
387  errno = 0;
388  int overrun = timer_getoverrun(impl->timerid);
389 
390  struct sllist queue;
391  sllist_init(&queue);
392 
393 #if !LELY_NO_THREADS
394  pthread_mutex_lock(&impl->mtx);
395 #endif
396  sllist_append(&queue, &impl->wait_queue);
397 #if !LELY_NO_THREADS
398  pthread_mutex_unlock(&impl->mtx);
399 #endif
400 
401  io_timer_wait_queue_post(&queue, overrun, errno);
402  errno = errsv;
403 }
404 
405 static inline struct io_timer_impl *
406 io_timer_impl_from_dev(const io_dev_t *dev)
407 {
408  assert(dev);
409 
410  return structof(dev, struct io_timer_impl, dev_vptr);
411 }
412 
413 static inline struct io_timer_impl *
414 io_timer_impl_from_timer(const io_timer_t *timer)
415 {
416  assert(timer);
417 
418  return structof(timer, struct io_timer_impl, timer_vptr);
419 }
420 
421 static inline struct io_timer_impl *
422 io_timer_impl_from_svc(const struct io_svc *svc)
423 {
424  assert(svc);
425 
426  return structof(svc, struct io_timer_impl, svc);
427 }
428 
429 static void
430 io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
431  struct ev_task *task)
432 {
433  assert(impl);
434  assert(queue);
435 
436 #if !LELY_NO_THREADS
437  pthread_mutex_lock(&impl->mtx);
438 #endif
439  if (!task)
440  sllist_append(queue, &impl->wait_queue);
441  else if (sllist_remove(&impl->wait_queue, &task->_node))
442  sllist_push_back(queue, &task->_node);
443 #if !LELY_NO_THREADS
444  pthread_mutex_unlock(&impl->mtx);
445 #endif
446 }
447 
448 #endif // _POSIX_C_SOURCE >= 199309L
ctx.h
io_timer_create
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:239
ev_exec_t
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition: ev.h:29
sllist_remove
struct slnode * sllist_remove(struct sllist *list, struct slnode *node)
Removes a node from a singly-linked list.
Definition: sllist.c:46
io_svc_vtbl
The virtual table of an I/O service.
Definition: ctx.h:67
IO_SVC_INIT
#define IO_SVC_INIT(vptr)
The static initializer for io_svc.
Definition: ctx.h:57
io_dev_vtbl
Definition: dev.h:41
util.h
io_ctx_insert
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
poll.h
sllist_append
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
io_timer_wait
A wait operation suitable for use with an I/O timer.
Definition: timer.h:54
IO_CLOCK_REALTIME
#define IO_CLOCK_REALTIME
The io_clock_t pointer representing the POSIX realtime clock.
Definition: clock.h:36
io_timer_destroy
void io_timer_destroy(io_timer_t *timer)
Destroys an I/O system timer.
Definition: timer.c:266
__io_poll
An I/O polling interface.
Definition: poll.c:48
ev_task_queue_abort
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
io_poll_get_ctx
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_svc
An I/O service.
Definition: ctx.h:49
sllist_init
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
Definition: sllist.h:184
timer.h
io_timer_wait::task
struct ev_task task
The task (to be) submitted upon completion (or cancellation) of the wait operation.
Definition: timer.h:59
io_ctx_remove
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
sllist
A singly-linked list.
Definition: sllist.h:51
io.h
structof
#define structof(ptr, type, member)
Obtains the address of a structure from the address of one of its members.
Definition: util.h:93
ev_task
An executable task.
Definition: task.h:41
io_timer_vtbl
Definition: timer.h:74
io_dev_t
const struct io_dev_vtbl *const io_dev_t
An abstract I/O device.
Definition: dev.h:35
io_timer_impl
Definition: timer.c:94
ev_exec_on_task_init
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
io_ctx
Definition: ctx.c:35
io_timer_t
const struct io_timer_vtbl *const io_timer_t
An abstract timer.
Definition: timer.h:38
clock.h
stdlib.h
sllist_push_back
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
IO_CLOCK_MONOTONIC
#define IO_CLOCK_MONOTONIC
The io_clock_t pointer representing the POSIX monotonic clock.
Definition: clock.h:43
io_clock_t
const struct io_clock_vtbl *const io_clock_t
An abstract clock.
Definition: clock.h:36
ev_task::exec
ev_exec_t * exec
A pointer to the executor to which the task is (to be) submitted.
Definition: task.h:43