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 
48 static io_ctx_t *io_timer_impl_dev_get_ctx(const io_dev_t *dev);
49 static ev_exec_t *io_timer_impl_dev_get_exec(const io_dev_t *dev);
50 static size_t io_timer_impl_dev_cancel(io_dev_t *dev, struct ev_task *task);
51 static size_t io_timer_impl_dev_abort(io_dev_t *dev, struct ev_task *task);
52 
53 // clang-format off
54 static 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 
62 static io_dev_t *io_timer_impl_get_dev(const io_timer_t *timer);
63 static io_clock_t *io_timer_impl_get_clock(const io_timer_t *timer);
64 static int io_timer_impl_getoverrun(const io_timer_t *timer);
65 static int io_timer_impl_gettime(
66  const io_timer_t *timer, struct itimerspec *value);
67 static int io_timer_impl_settime(io_timer_t *timer, int flags,
68  const struct itimerspec *value, struct itimerspec *ovalue);
69 static void io_timer_impl_submit_wait(
70  io_timer_t *timer, struct io_timer_wait *wait);
71 
72 // clang-format off
73 static 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 
83 static int io_timer_impl_svc_notify_fork(
84  struct io_svc *svc, enum io_fork_event e);
85 static void io_timer_impl_svc_shutdown(struct io_svc *svc);
86 
87 // clang-format off
88 static 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 
94 struct io_timer_impl {
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 
114 static void io_timer_impl_watch_func(struct io_poll_watch *watch, int events);
115 static void io_timer_impl_wait_task_func(struct ev_task *task);
116 
117 static inline struct io_timer_impl *io_timer_impl_from_dev(const io_dev_t *dev);
118 static inline struct io_timer_impl *io_timer_impl_from_timer(
119  const io_timer_t *timer);
120 static inline struct io_timer_impl *io_timer_impl_from_svc(
121  const struct io_svc *svc);
122 
123 static void io_timer_impl_pop(struct io_timer_impl *impl, struct sllist *queue,
124  struct ev_task *task);
125 
126 static int io_timer_impl_open(struct io_timer_impl *impl);
127 static int io_timer_impl_close(struct io_timer_impl *impl);
128 
129 void *
130 io_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 
141 void
142 io_timer_free(void *ptr)
143 {
144  if (ptr)
145  free(io_timer_impl_from_timer(ptr));
146 }
147 
148 io_timer_t *
149 io_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);
201 error_open:
202 #if !LELY_NO_THREADS
203  pthread_mutex_destroy(&impl->mtx);
204 error_init_mtx:
205 #endif
206  errno = errsv;
207  return NULL;
208 }
209 
210 void
211 io_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 
242 io_timer_t *
243 io_timer_create(io_poll_t *poll, ev_exec_t *exec, clockid_t clockid)
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 
262 error_init:
263  io_timer_free((void *)timer);
264 error_alloc:
265  errno = errsv;
266  return NULL;
267 }
268 
269 void
271 {
272  if (timer) {
273  io_timer_fini(timer);
274  io_timer_free((void *)timer);
275  }
276 }
277 
278 static io_ctx_t *
279 io_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 
286 static ev_exec_t *
287 io_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 
294 static size_t
295 io_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 
307 static size_t
308 io_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 
320 static io_dev_t *
321 io_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 
328 static io_clock_t *
329 io_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 
342 static int
343 io_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 
358 static int
359 io_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 
366 static int
367 io_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 
379 static void
380 io_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;
388  ev_exec_on_task_init(task->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 
406 static int
407 io_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 
442 static void
443 io_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 
472 static void
473 io_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 
494 static void
495 io_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 
549 static inline struct io_timer_impl *
550 io_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 
557 static inline struct io_timer_impl *
558 io_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 
565 static inline struct io_timer_impl *
566 io_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 
573 static void
574 io_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 
592 static int
593 io_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 
618 error_poll_watch:
619  close(impl->tfd);
620  impl->tfd = -1;
621 error_timerfd_create:
622 error_close:
623  errno = errsv;
624  return -1;
625 }
626 
627 static int
628 io_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__
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:243
ev_exec_t
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition: ev.h:29
IO_POLL_WATCH_INIT
#define IO_POLL_WATCH_INIT(func)
The static initializer for io_poll_watch.
Definition: poll.h:65
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_poll_watch
An object representing a file descriptor being monitored for I/O events.
Definition: poll.h:56
IO_EVENT_IN
@ IO_EVENT_IN
Data (other than priority data) MAY be read without blocking.
Definition: event.h:35
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:126
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:257
io_timer_wait
A wait operation suitable for use with an I/O timer.
Definition: timer.h:54
io.h
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:270
__io_poll
An I/O polling interface.
Definition: poll.c:51
io_poll_watch
int io_poll_watch(io_poll_t *poll, io_handle_t handle, struct io_event *event, int keep)
Registers an I/O device with an I/O polling interface and instructs it to watch for certain events.
Definition: poll.c:252
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:194
timer.h
ev_exec_abort
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
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:141
sllist
A singly-linked list.
Definition: sllist.h:52
unistd.h
io_fork_event
io_fork_event
The type of event generated by an I/O context before and after a process fork.
Definition: ctx.h:37
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
EV_TASK_INIT
#define EV_TASK_INIT(exec, func)
The static initializer for ev_task.
Definition: task.h:53
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:106
io_ctx
Definition: ctx.c:38
io_timer_t
const struct io_timer_vtbl *const io_timer_t
An abstract timer.
Definition: timer.h:38
clock.h
ev_exec_post
void ev_exec_post(ev_exec_t *exec, struct ev_task *task)
Submits *task to *exec for execution.
Definition: exec.h:124
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:232
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
IO_FORK_CHILD
@ IO_FORK_CHILD
The event generated after the fork in the child process.
Definition: ctx.h:43