Lely core libraries  2.3.4
sigset.c
Go to the documentation of this file.
1 
24 #include "io.h"
25 
26 #if !LELY_NO_STDIO && _POSIX_C_SOURCE >= 200112L
27 
28 #include "../sigset.h"
29 #include "fd.h"
30 #include <lely/io2/ctx.h>
31 #include <lely/io2/posix/poll.h>
32 #include <lely/io2/sys/sigset.h>
33 #include <lely/util/diag.h>
34 #include <lely/util/util.h>
35 
36 #include <assert.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include <fcntl.h>
42 #if !LELY_NO_THREADS
43 #include <pthread.h>
44 #include <sched.h>
45 #endif
46 #include <unistd.h>
47 
48 #ifndef LELY_NSIG
49 #ifdef _NSIG
50 #define LELY_NSIG _NSIG
51 #else
52 #define LELY_NSIG 128
53 #endif
54 #endif
55 
57  struct io_sigset_node *next;
58  unsigned signo : 30;
59  unsigned watched : 1;
60  unsigned pending : 1;
61 };
62 
63 #define IO_SIGSET_NODE_INIT(signo) \
64  { \
65  NULL, (signo), 0, 0 \
66  }
67 
68 static struct {
69 #if !LELY_NO_THREADS
70  pthread_mutex_t mtx;
71 #endif
72  sig_atomic_t pending;
73  struct {
74  sig_atomic_t pending;
75  sig_atomic_t fd;
76  struct io_sigset_node *list;
77  } sig[LELY_NSIG - 1];
78 } io_sigset_shared = {
79 #if !LELY_NO_THREADS
80  PTHREAD_MUTEX_INITIALIZER,
81 #endif
82  0, { { 0, 0, NULL } }
83 };
84 
85 static struct sigaction io_sigset_action[LELY_NSIG - 1];
86 
87 static void io_sigset_handler(int signo);
88 static void io_sigset_kill(int signo);
89 static void io_sigset_process_all(void);
90 static void io_sigset_process_sig(int signo, struct sllist *queue);
91 
92 static io_ctx_t *io_sigset_impl_dev_get_ctx(const io_dev_t *dev);
93 static ev_exec_t *io_sigset_impl_dev_get_exec(const io_dev_t *dev);
94 static size_t io_sigset_impl_dev_cancel(io_dev_t *dev, struct ev_task *task);
95 static size_t io_sigset_impl_dev_abort(io_dev_t *dev, struct ev_task *task);
96 
97 // clang-format off
98 static const struct io_dev_vtbl io_sigset_impl_dev_vtbl = {
99  &io_sigset_impl_dev_get_ctx,
100  &io_sigset_impl_dev_get_exec,
101  &io_sigset_impl_dev_cancel,
102  &io_sigset_impl_dev_abort
103 };
104 // clang-format on
105 
106 static io_dev_t *io_sigset_impl_get_dev(const io_sigset_t *sigset);
107 static int io_sigset_impl_clear(io_sigset_t *sigset);
108 static int io_sigset_impl_insert(io_sigset_t *sigset, int signo);
109 static int io_sigset_impl_remove(io_sigset_t *sigset, int signo);
110 static void io_sigset_impl_submit_wait(
111  io_sigset_t *sigset, struct io_sigset_wait *wait);
112 
113 // clang-format off
114 static const struct io_sigset_vtbl io_sigset_impl_vtbl = {
115  &io_sigset_impl_get_dev,
116  &io_sigset_impl_clear,
117  &io_sigset_impl_insert,
118  &io_sigset_impl_remove,
119  &io_sigset_impl_submit_wait
120 };
121 // clang-format on
122 
123 static int io_sigset_impl_svc_notify_fork(
124  struct io_svc *svc, enum io_fork_event e);
125 static void io_sigset_impl_svc_shutdown(struct io_svc *svc);
126 
127 // clang-format off
128 static const struct io_svc_vtbl io_sigset_impl_svc_vtbl = {
129  &io_sigset_impl_svc_notify_fork,
130  &io_sigset_impl_svc_shutdown
131 };
132 // clang-format on
133 
135  const struct io_dev_vtbl *dev_vptr;
136  const struct io_sigset_vtbl *sigset_vptr;
137  io_poll_t *poll;
138  struct io_svc svc;
139  io_ctx_t *ctx;
140  ev_exec_t *exec;
141  struct io_poll_watch watch;
142  int fds[2];
143  struct ev_task read_task;
144  struct ev_task wait_task;
145 #if !LELY_NO_THREADS
146  pthread_mutex_t mtx;
147 #endif
148  unsigned shutdown : 1;
149  unsigned read_posted : 1;
150  unsigned wait_posted : 1;
151  unsigned pending : 1;
152  struct sllist queue;
153  struct io_sigset_node nodes[LELY_NSIG - 1];
154 };
155 
156 static void io_sigset_impl_watch_func(struct io_poll_watch *watch, int events);
157 static void io_sigset_impl_read_task_func(struct ev_task *task);
158 static void io_sigset_impl_wait_task_func(struct ev_task *task);
159 
160 static inline struct io_sigset_impl *io_sigset_impl_from_dev(
161  const io_dev_t *dev);
162 static inline struct io_sigset_impl *io_sigset_impl_from_sigset(
163  const io_sigset_t *sigset);
164 static inline struct io_sigset_impl *io_sigset_impl_from_svc(
165  const struct io_svc *svc);
166 
167 static void io_sigset_impl_pop(struct io_sigset_impl *impl,
168  struct sllist *queue, struct ev_task *task);
169 
170 static int io_sigset_impl_open(struct io_sigset_impl *impl);
171 static int io_sigset_impl_close(struct io_sigset_impl *impl);
172 
173 static int io_sigset_impl_do_insert(struct io_sigset_impl *impl, int signo);
174 static int io_sigset_impl_do_remove(struct io_sigset_impl *impl, int signo);
175 
176 static size_t io_sigset_impl_do_abort_tasks(struct io_sigset_impl *impl);
177 
178 void *
179 io_sigset_alloc(void)
180 {
181  struct io_sigset_impl *impl = malloc(sizeof(*impl));
182  if (!impl)
183  return NULL;
184  // Suppress a GCC maybe-uninitialized warning.
185  impl->sigset_vptr = NULL;
186  // cppcheck-suppress memleak symbolName=impl
187  return &impl->sigset_vptr;
188 }
189 
190 void
191 io_sigset_free(void *ptr)
192 {
193  if (ptr)
194  free(io_sigset_impl_from_sigset(ptr));
195 }
196 
197 io_sigset_t *
198 io_sigset_init(io_sigset_t *sigset, io_poll_t *poll, ev_exec_t *exec)
199 {
200  struct io_sigset_impl *impl = io_sigset_impl_from_sigset(sigset);
201  assert(poll);
202  assert(exec);
203  io_ctx_t *ctx = io_poll_get_ctx(poll);
204  assert(ctx);
205 
206  int errsv = 0;
207 
208  impl->dev_vptr = &io_sigset_impl_dev_vtbl;
209  impl->sigset_vptr = &io_sigset_impl_vtbl;
210 
211  impl->poll = poll;
212 
213  impl->svc = (struct io_svc)IO_SVC_INIT(&io_sigset_impl_svc_vtbl);
214  impl->ctx = ctx;
215 
216  impl->exec = exec;
217 
218  impl->watch = (struct io_poll_watch)IO_POLL_WATCH_INIT(
219  &io_sigset_impl_watch_func);
220  impl->fds[0] = impl->fds[1] = -1;
221 
222  impl->read_task = (struct ev_task)EV_TASK_INIT(
223  impl->exec, &io_sigset_impl_read_task_func);
224  impl->wait_task = (struct ev_task)EV_TASK_INIT(
225  impl->exec, &io_sigset_impl_wait_task_func);
226 
227 #if !LELY_NO_THREADS
228  if ((errsv = pthread_mutex_init(&impl->mtx, NULL)))
229  goto error_init_mtx;
230 #endif
231 
232  impl->shutdown = 0;
233  impl->read_posted = 0;
234  impl->wait_posted = 0;
235  impl->pending = 0;
236 
237  sllist_init(&impl->queue);
238 
239  for (int i = 1; i < LELY_NSIG; i++)
240  impl->nodes[i - 1] =
241  (struct io_sigset_node)IO_SIGSET_NODE_INIT(i);
242 
243  if (io_sigset_impl_open(impl) == -1) {
244  errsv = errno;
245  goto error_open;
246  }
247 
248  io_ctx_insert(impl->ctx, &impl->svc);
249 
250  return sigset;
251 
252  // io_sigset_impl_close(impl);
253 error_open:
254 #if !LELY_NO_THREADS
255  pthread_mutex_destroy(&impl->mtx);
256 error_init_mtx:
257 #endif
258  errno = errsv;
259  return NULL;
260 }
261 
262 void
263 io_sigset_fini(io_sigset_t *sigset)
264 {
265  struct io_sigset_impl *impl = io_sigset_impl_from_sigset(sigset);
266 
267  io_ctx_remove(impl->ctx, &impl->svc);
268  // Cancel all pending tasks.
269  io_sigset_impl_svc_shutdown(&impl->svc);
270 
271  // Stop monitoring signals.
272  io_sigset_impl_clear(sigset);
273 
274 #if !LELY_NO_THREADS
275  int warning = 0;
276  pthread_mutex_lock(&impl->mtx);
277  // If necessary, busy-wait until io_sigset_impl_read_task_func() and
278  // io_sigset_impl_wait_task_func() complete.
279  while (impl->read_posted || impl->wait_posted) {
280  if (io_sigset_impl_do_abort_tasks(impl))
281  continue;
282  pthread_mutex_unlock(&impl->mtx);
283  if (!warning) {
284  warning = 1;
285  diag(DIAG_WARNING, 0,
286  "io_sigset_fini() invoked with pending operations");
287  }
288  sched_yield();
289  pthread_mutex_lock(&impl->mtx);
290  }
291  pthread_mutex_unlock(&impl->mtx);
292 #endif
293 
294  // Close the self-pipe.
295  io_sigset_impl_close(impl);
296 
297 #if !LELY_NO_THREADS
298  pthread_mutex_destroy(&impl->mtx);
299 #endif
300 }
301 
302 io_sigset_t *
304 {
305  int errsv = 0;
306 
307  io_sigset_t *sigset = io_sigset_alloc();
308  if (!sigset) {
309  errsv = errno;
310  goto error_alloc;
311  }
312 
313  io_sigset_t *tmp = io_sigset_init(sigset, poll, exec);
314  if (!tmp) {
315  errsv = errno;
316  goto error_init;
317  }
318  sigset = tmp;
319 
320  return sigset;
321 
322 error_init:
323  io_sigset_free((void *)sigset);
324 error_alloc:
325  errno = errsv;
326  return NULL;
327 }
328 
329 void
331 {
332  if (sigset) {
333  io_sigset_fini(sigset);
334  io_sigset_free((void *)sigset);
335  }
336 }
337 
338 static void
339 io_sigset_handler(int signo)
340 {
341  io_sigset_shared.sig[signo - 1].pending = 1;
342  io_sigset_shared.pending = 1;
343 
344  io_sigset_kill(signo);
345 }
346 
347 static void
348 io_sigset_kill(int signo)
349 {
350  int errsv = errno;
351  ssize_t result;
352  do {
353  errno = 0;
354  result = write(io_sigset_shared.sig[signo - 1].fd - 1, "", 1);
355  } while (result == -1 && errno == EINTR);
356  errno = errsv;
357 }
358 
359 static void
360 io_sigset_process_all(void)
361 {
362  struct sllist queue;
363  sllist_init(&queue);
364 
365 #if !LELY_NO_THREADS
366  pthread_mutex_lock(&io_sigset_shared.mtx);
367 #endif
368  while (io_sigset_shared.pending) {
369  io_sigset_shared.pending = 0;
370  for (int i = 1; i < LELY_NSIG; i++) {
371  if (io_sigset_shared.sig[i - 1].pending) {
372  io_sigset_shared.sig[i - 1].pending = 0;
373  io_sigset_process_sig(i, &queue);
374  }
375  }
376  }
377 #if !LELY_NO_THREADS
378  pthread_mutex_unlock(&io_sigset_shared.mtx);
379 #endif
380 
381  struct slnode *node;
382  while ((node = sllist_pop_front(&queue))) {
383  struct ev_task *task = ev_task_from_node(node);
384  ev_exec_post(task->exec, task);
385  }
386 }
387 
388 static void
389 io_sigset_process_sig(int signo, struct sllist *queue)
390 {
391  for (struct io_sigset_node *node = io_sigset_shared.sig[signo - 1].list;
392  node; node = node->next) {
393  struct io_sigset_impl *impl = structof(
394  node, struct io_sigset_impl, nodes[signo - 1]);
395 #if !LELY_NO_THREADS
396  pthread_mutex_lock(&impl->mtx);
397 #endif
398  assert(node->watched);
399  if (!node->pending) {
400  node->pending = 1;
401  impl->pending = 1;
402  if (!impl->wait_posted) {
403  impl->wait_posted = 1;
404  sllist_push_back(queue, &impl->wait_task._node);
405  }
406  }
407 #if !LELY_NO_THREADS
408  pthread_mutex_unlock(&impl->mtx);
409 #endif
410  }
411 }
412 
413 static io_ctx_t *
414 io_sigset_impl_dev_get_ctx(const io_dev_t *dev)
415 {
416  const struct io_sigset_impl *impl = io_sigset_impl_from_dev(dev);
417 
418  return impl->ctx;
419 }
420 
421 static ev_exec_t *
422 io_sigset_impl_dev_get_exec(const io_dev_t *dev)
423 {
424  const struct io_sigset_impl *impl = io_sigset_impl_from_dev(dev);
425 
426  return impl->exec;
427 }
428 
429 static size_t
430 io_sigset_impl_dev_cancel(io_dev_t *dev, struct ev_task *task)
431 {
432  struct io_sigset_impl *impl = io_sigset_impl_from_dev(dev);
433 
434  struct sllist queue;
435  sllist_init(&queue);
436 
437  io_sigset_impl_pop(impl, &queue, task);
438 
439  return io_sigset_wait_queue_post(&queue, 0);
440 }
441 
442 static size_t
443 io_sigset_impl_dev_abort(io_dev_t *dev, struct ev_task *task)
444 {
445  struct io_sigset_impl *impl = io_sigset_impl_from_dev(dev);
446 
447  struct sllist queue;
448  sllist_init(&queue);
449 
450  io_sigset_impl_pop(impl, &queue, task);
451 
452  return ev_task_queue_abort(&queue);
453 }
454 
455 static io_dev_t *
456 io_sigset_impl_get_dev(const io_sigset_t *sigset)
457 {
458  const struct io_sigset_impl *impl = io_sigset_impl_from_sigset(sigset);
459 
460  return &impl->dev_vptr;
461 }
462 
463 static int
464 io_sigset_impl_clear(io_sigset_t *sigset)
465 {
466  struct io_sigset_impl *impl = io_sigset_impl_from_sigset(sigset);
467 
468 #if !LELY_NO_THREADS
469  // Locking this mutex may fail since it is statically initialized.
470  int errsv = pthread_mutex_lock(&io_sigset_shared.mtx);
471  if (errsv) {
472  errno = errsv;
473  return -1;
474  }
475 #endif
476 
477  int result = 0;
478  for (int i = 1; i < LELY_NSIG; i++) {
479  if (io_sigset_impl_do_remove(impl, i) == -1)
480  result = -1;
481  }
482 
483 #if !LELY_NO_THREADS
484  pthread_mutex_unlock(&io_sigset_shared.mtx);
485 #endif
486 
487  return result;
488 }
489 
490 static int
491 io_sigset_impl_insert(io_sigset_t *sigset, int signo)
492 {
493  struct io_sigset_impl *impl = io_sigset_impl_from_sigset(sigset);
494 
495  if (signo <= 0 || signo >= LELY_NSIG) {
496  errno = EINVAL;
497  return -1;
498  }
499 
500 #if !LELY_NO_THREADS
501  // Locking this mutex may fail since it is statically initialized.
502  int errsv = pthread_mutex_lock(&io_sigset_shared.mtx);
503  if (errsv) {
504  errno = errsv;
505  return -1;
506  }
507 #endif
508 
509  int result = io_sigset_impl_do_insert(impl, signo);
510 
511 #if !LELY_NO_THREADS
512  pthread_mutex_unlock(&io_sigset_shared.mtx);
513 #endif
514 
515  return result;
516 }
517 
518 static int
519 io_sigset_impl_remove(io_sigset_t *sigset, int signo)
520 {
521  struct io_sigset_impl *impl = io_sigset_impl_from_sigset(sigset);
522 
523  if (signo <= 0 || signo >= LELY_NSIG) {
524  errno = EINVAL;
525  return -1;
526  }
527 
528 #if !LELY_NO_THREADS
529  // Locking this mutex may fail since it is statically initialized.
530  int errsv = pthread_mutex_lock(&io_sigset_shared.mtx);
531  if (errsv) {
532  errno = errsv;
533  return -1;
534  }
535 #endif
536 
537  int result = io_sigset_impl_do_remove(impl, signo);
538 
539 #if !LELY_NO_THREADS
540  pthread_mutex_unlock(&io_sigset_shared.mtx);
541 #endif
542 
543  return result;
544 }
545 
546 static void
547 io_sigset_impl_submit_wait(io_sigset_t *sigset, struct io_sigset_wait *wait)
548 {
549  struct io_sigset_impl *impl = io_sigset_impl_from_sigset(sigset);
550  assert(wait);
551  struct ev_task *task = &wait->task;
552 
553  if (!task->exec)
554  task->exec = impl->exec;
555  ev_exec_on_task_init(task->exec);
556 
557 #if !LELY_NO_THREADS
558  pthread_mutex_lock(&impl->mtx);
559 #endif
560  if (impl->shutdown) {
561 #if !LELY_NO_THREADS
562  pthread_mutex_unlock(&impl->mtx);
563 #endif
564  io_sigset_wait_post(wait, 0);
565  } else {
566  sllist_push_back(&impl->queue, &task->_node);
567  int post_wait = !impl->wait_posted && impl->pending;
568  if (post_wait)
569  impl->wait_posted = 1;
570 #if !LELY_NO_THREADS
571  pthread_mutex_unlock(&impl->mtx);
572 #endif
573  if (post_wait)
574  ev_exec_post(impl->wait_task.exec, &impl->wait_task);
575  }
576 }
577 
578 static int
579 io_sigset_impl_svc_notify_fork(struct io_svc *svc, enum io_fork_event e)
580 {
581  struct io_sigset_impl *impl = io_sigset_impl_from_svc(svc);
582 
583  if (e != IO_FORK_CHILD || impl->shutdown)
584  return 0;
585 
586  int result = 0;
587  int errsv = errno;
588 
589  if (io_sigset_impl_close(impl) == -1 && !result) {
590  errsv = errno;
591  result = -1;
592  }
593 
594  if (io_sigset_impl_open(impl) == -1 && !result) {
595  errsv = errno;
596  result = -1;
597  }
598 
599  errno = errsv;
600  return result;
601 }
602 
603 static void
604 io_sigset_impl_svc_shutdown(struct io_svc *svc)
605 {
606  struct io_sigset_impl *impl = io_sigset_impl_from_svc(svc);
607  io_dev_t *dev = &impl->dev_vptr;
608 
609 #if !LELY_NO_THREADS
610  pthread_mutex_lock(&impl->mtx);
611 #endif
612  int shutdown = !impl->shutdown;
613  impl->shutdown = 1;
614  if (shutdown) {
615  // Stop monitoring the self-pipe.
616  io_poll_watch(impl->poll, impl->fds[0], 0, &impl->watch);
617  // Try to abort io_sigset_impl_read_task_func() and
618  // io_sigset_impl_wait_task_func().
619  io_sigset_impl_do_abort_tasks(impl);
620  }
621 #if !LELY_NO_THREADS
622  pthread_mutex_unlock(&impl->mtx);
623 #endif
624 
625  if (shutdown)
626  // Cancel all pending operations.
627  io_sigset_impl_dev_cancel(dev, NULL);
628 }
629 
630 static void
631 io_sigset_impl_watch_func(struct io_poll_watch *watch, int events)
632 {
633  assert(watch);
634  struct io_sigset_impl *impl =
635  structof(watch, struct io_sigset_impl, watch);
636  (void)events;
637 
638 #if !LELY_NO_THREADS
639  pthread_mutex_lock(&impl->mtx);
640 #endif
641  int post_read = !impl->read_posted;
642  impl->read_posted = 1;
643 #if !LELY_NO_THREADS
644  pthread_mutex_unlock(&impl->mtx);
645 #endif
646 
647  if (post_read)
648  ev_exec_post(impl->read_task.exec, &impl->read_task);
649 }
650 
651 static void
652 io_sigset_impl_read_task_func(struct ev_task *task)
653 {
654  assert(task);
655  struct io_sigset_impl *impl =
656  structof(task, struct io_sigset_impl, read_task);
657 
658  int errsv = errno;
659  int pending = 0;
660  int events = 0;
661 
662  ssize_t result;
663  do {
664  char buf[LELY_VLA_SIZE_MAX];
665  errno = 0;
666  result = read(impl->fds[0], buf, sizeof(buf));
667  if (result > 0)
668  pending = 1;
669  } while (result > 0 || (result == -1 && errno == EINTR));
670  if (result == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
671  events |= IO_EVENT_IN;
672 
673  if (pending)
674  io_sigset_process_all();
675 
676 #if !LELY_NO_THREADS
677  pthread_mutex_lock(&impl->mtx);
678 #endif
679  if (events && !impl->shutdown)
680  io_poll_watch(impl->poll, impl->fds[0], events, &impl->watch);
681  impl->read_posted = 0;
682 #if !LELY_NO_THREADS
683  pthread_mutex_unlock(&impl->mtx);
684 #endif
685 
686  errno = errsv;
687 }
688 
689 static void
690 io_sigset_impl_wait_task_func(struct ev_task *task)
691 {
692  assert(task);
693  struct io_sigset_impl *impl =
694  structof(task, struct io_sigset_impl, wait_task);
695 
696  struct io_sigset_wait *wait = NULL;
697  int signo = LELY_NSIG;
698 
699 #if !LELY_NO_THREADS
700  pthread_mutex_lock(&impl->mtx);
701 #endif
702  if (!sllist_empty(&impl->queue)) {
703  for (signo = 1; signo < LELY_NSIG; signo++) {
704  struct io_sigset_node *node = &impl->nodes[signo - 1];
705  if (node->pending) {
706  node->pending = 0;
707  task = ev_task_from_node(
708  sllist_pop_front(&impl->queue));
709  wait = io_sigset_wait_from_task(task);
710  break;
711  }
712  }
713  }
714 
715  impl->pending = signo != LELY_NSIG;
716  int post_wait = impl->wait_posted = impl->pending
717  && !sllist_empty(&impl->queue) && !impl->shutdown;
718 #if !LELY_NO_THREADS
719  pthread_mutex_unlock(&impl->mtx);
720 #endif
721 
722  if (wait)
723  io_sigset_wait_post(wait, signo);
724 
725  if (post_wait)
726  ev_exec_post(impl->wait_task.exec, &impl->wait_task);
727 }
728 
729 static inline struct io_sigset_impl *
730 io_sigset_impl_from_dev(const io_dev_t *dev)
731 {
732  assert(dev);
733 
734  return structof(dev, struct io_sigset_impl, dev_vptr);
735 }
736 
737 static inline struct io_sigset_impl *
738 io_sigset_impl_from_sigset(const io_sigset_t *sigset)
739 {
740  assert(sigset);
741 
742  return structof(sigset, struct io_sigset_impl, sigset_vptr);
743 }
744 
745 static inline struct io_sigset_impl *
746 io_sigset_impl_from_svc(const struct io_svc *svc)
747 {
748  assert(svc);
749 
750  return structof(svc, struct io_sigset_impl, svc);
751 }
752 
753 static void
754 io_sigset_impl_pop(struct io_sigset_impl *impl, struct sllist *queue,
755  struct ev_task *task)
756 {
757  assert(impl);
758  assert(queue);
759 
760 #if !LELY_NO_THREADS
761  pthread_mutex_lock(&impl->mtx);
762 #endif
763  if (!task)
764  sllist_append(queue, &impl->queue);
765  else if (sllist_remove(&impl->queue, &task->_node))
766  sllist_push_back(queue, &task->_node);
767 #if !LELY_NO_THREADS
768  pthread_mutex_unlock(&impl->mtx);
769 #endif
770 }
771 
772 static int
773 io_sigset_impl_open(struct io_sigset_impl *impl)
774 {
775  assert(impl);
776 
777  int errsv = 0;
778 
779  if (io_sigset_impl_close(impl) == -1) {
780  errsv = errno;
781  goto error_close;
782  }
783 
784 #ifdef __linux__
785  if (pipe2(impl->fds, O_CLOEXEC | O_NONBLOCK) == -1) {
786 #else
787  if (pipe(impl->fds) == -1) {
788 #endif
789  errsv = errno;
790  goto error_pipe;
791  }
792 
793  // We need to be able to store the write end of the pipe in a
794  // sig_atomic_t variable.
795  if (impl->fds[1] - 1 > SIG_ATOMIC_MAX) {
796  errno = EBADF;
797  goto error_sig_atomic;
798  }
799 
800 #ifndef __linux__
801  if (io_fd_set_cloexec(impl->fds[0]) == -1) {
802  errsv = errno;
803  goto error_cloexec;
804  }
805 
806  if (io_fd_set_cloexec(impl->fds[1]) == -1) {
807  errsv = errno;
808  goto error_cloexec;
809  }
810 
811  if (io_fd_set_nonblock(impl->fds[0]) == -1) {
812  errsv = errno;
813  goto error_nonblock;
814  }
815 
816  if (io_fd_set_nonblock(impl->fds[1]) == -1) {
817  errsv = errno;
818  goto error_nonblock;
819  }
820 #endif // !__linux__
821 
822  // clang-format off
823  if (io_poll_watch(impl->poll, impl->fds[0], IO_EVENT_IN, &impl->watch)
824  == -1) {
825  // clang-format on
826  errsv = errno;
827  goto error_poll_watch;
828  }
829 
830  return 0;
831 
832 error_poll_watch:
833 #ifndef __linux__
834 error_nonblock:
835 error_cloexec:
836 #endif
837 error_sig_atomic:
838  close(impl->fds[1]);
839  close(impl->fds[0]);
840  impl->fds[0] = impl->fds[1] = -1;
841 error_pipe:
842 error_close:
843  errno = errsv;
844  return -1;
845 }
846 
847 static int
848 io_sigset_impl_close(struct io_sigset_impl *impl)
849 {
850  assert(impl);
851 
852  int fds[2] = { impl->fds[0], impl->fds[1] };
853  if (fds[0] == -1)
854  return 0;
855  impl->fds[0] = impl->fds[1] = -1;
856 
857  int result = 0;
858  int errsv = errno;
859 
860  if (!impl->shutdown
861  && io_poll_watch(impl->poll, fds[0], 0, &impl->watch)
862  == -1
863  && !result) {
864  errsv = errno;
865  result = -1;
866  }
867 
868  if (close(fds[1]) == -1 && !result) {
869  errsv = errno;
870  result = -1;
871  }
872 
873  if (close(fds[0]) == -1 && !result) {
874  errsv = errno;
875  result = -1;
876  }
877 
878  errno = errsv;
879  return result;
880 }
881 
882 static int
883 io_sigset_impl_do_insert(struct io_sigset_impl *impl, int signo)
884 {
885  assert(impl);
886  assert(signo > 0);
887  assert(signo < LELY_NSIG);
888 
889  struct io_sigset_node *node = &impl->nodes[signo - 1];
890  assert(node->signo == signo);
891 
892  if (node->watched)
893  return 0;
894 
895  if (!io_sigset_shared.sig[signo - 1].list) {
896  assert(!io_sigset_shared.sig[signo - 1].fd);
897  io_sigset_shared.sig[signo - 1].fd = impl->fds[1] + 1;
898 
899  struct sigaction act;
900  memset(&act, 0, sizeof(act));
901  act.sa_handler = &io_sigset_handler;
902  sigemptyset(&act.sa_mask);
903  act.sa_flags = SA_RESTART;
904 
905  if (sigaction(signo, &act, &io_sigset_action[signo - 1])
906  == -1) {
907  io_sigset_shared.sig[signo - 1].fd = 0;
908  return -1;
909  }
910  }
911 
912  node->next = io_sigset_shared.sig[signo - 1].list;
913  io_sigset_shared.sig[signo - 1].list = node;
914 
915  node->watched = 1;
916  assert(!node->pending);
917 
918  return 0;
919 }
920 
921 static int
922 io_sigset_impl_do_remove(struct io_sigset_impl *impl, int signo)
923 {
924  assert(impl);
925  assert(signo > 0);
926  assert(signo < LELY_NSIG);
927 
928  struct io_sigset_node *node = &impl->nodes[signo - 1];
929  assert(node->signo == signo);
930 
931  if (!node->watched)
932  return 0;
933 
934  struct io_sigset_node **pnode = &io_sigset_shared.sig[signo - 1].list;
935  assert(*pnode);
936  while (*pnode != node)
937  pnode = &(*pnode)->next;
938  assert(*pnode == node);
939  *pnode = node->next;
940  node->next = NULL;
941 
942  node->watched = 0;
943  node->pending = 0;
944 
945  int result = 0;
946 
947  if (!*pnode) {
948  assert(io_sigset_shared.sig[signo - 1].fd == impl->fds[1] + 1);
949  if (pnode == &io_sigset_shared.sig[signo - 1].list) {
950  result = sigaction(signo, &io_sigset_action[signo - 1],
951  NULL);
952 
953  io_sigset_shared.sig[signo - 1].fd = 0;
954  } else {
955  node = structof(pnode, struct io_sigset_node, next);
956  impl = structof(node, struct io_sigset_impl,
957  nodes[signo - 1]);
958  io_sigset_shared.sig[signo - 1].fd = impl->fds[1] + 1;
959  io_sigset_kill(signo);
960  }
961  }
962 
963  return result;
964 }
965 
966 static size_t
967 io_sigset_impl_do_abort_tasks(struct io_sigset_impl *impl)
968 {
969  assert(impl);
970 
971  size_t n = 0;
972 
973  // Try to abort io_sigset_impl_read_task_func().
974  // clang-format off
975  if (impl->read_posted && ev_exec_abort(impl->read_task.exec,
976  &impl->read_task)) {
977  // clang-format on
978  impl->read_posted = 0;
979  n++;
980  }
981 
982  // Try to abort io_sigset_impl_wait_task_func().
983  // clang-format off
984  if (impl->wait_posted && ev_exec_abort(impl->wait_task.exec,
985  &impl->wait_task)) {
986  // clang-format on
987  impl->wait_posted = 0;
988  n++;
989  }
990 
991  return n;
992 }
993 
994 #endif // !LELY_NO_STDIO && _POSIX_C_SOURCE >= 200112L
ctx.h
pnode
A node in a pairing heap.
Definition: pheap.h:52
ev_task_from_node
struct ev_task * ev_task_from_node(struct slnode *node)
Converts a pointer to a node in a queue to the address of the task containing the node.
Definition: task.c:32
LELY_VLA_SIZE_MAX
#define LELY_VLA_SIZE_MAX
The maximum size (in bytes) of stack-allocated arrays.
Definition: features.h:354
io_fd_set_nonblock
int io_fd_set_nonblock(int fd)
Sets the O_NONBLOCK flag of the file descriptor fd.
Definition: fd.c:44
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
slnode::next
struct slnode * next
A pointer to the next node in the list.
Definition: sllist.h:42
io_svc_vtbl
The virtual table of an I/O service.
Definition: ctx.h:67
string.h
IO_SVC_INIT
#define IO_SVC_INIT(vptr)
The static initializer for io_svc.
Definition: ctx.h:57
io_sigset_wait
A wait operation suitable for use with a signal handler.
Definition: sigset.h:43
io_dev_vtbl
Definition: dev.h:41
util.h
diag.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_sigset_t
const struct io_sigset_vtbl *const io_sigset_t
An abstract signal handler.
Definition: sigset.h:40
pnode::next
struct pnode * next
A pointer to the next sibling node.
Definition: pheap.h:62
io_sigset_node
Definition: sigset.c:56
fd.h
sllist_empty
int sllist_empty(const struct sllist *list)
Returns 1 if the singly-linked list is empty, and 0 if not.
Definition: sllist.h:202
DIAG_WARNING
@ DIAG_WARNING
A warning.
Definition: diag.h:55
io_sigset_wait_from_task
struct io_sigset_wait * io_sigset_wait_from_task(struct ev_task *task)
Obtains a pointer to a signal wait operation from a pointer to its completion task.
Definition: sigset.c:62
__io_poll
An I/O polling interface.
Definition: poll.c:51
io_sigset_wait::signo
int signo
The signal number, or 0 if the wait operation was canceled.
Definition: sigset.h:50
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_sigset_destroy
void io_sigset_destroy(io_sigset_t *sigset)
Destroys a system signal handler.
Definition: sigset.c:330
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
sllist_pop_front
struct slnode * sllist_pop_front(struct sllist *list)
Pops a node from the front of a singly-linked list.
Definition: sllist.h:243
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_fd_set_cloexec
int io_fd_set_cloexec(int fd)
Sets the FD_CLOEXEC flag of the file descriptor fd.
Definition: fd.c:33
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
diag
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:171
sllist
A singly-linked list.
Definition: sllist.h:52
io.h
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
io_sigset_impl
Definition: sigset.c:134
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_dev_t
const struct io_dev_vtbl *const io_dev_t
An abstract I/O device.
Definition: dev.h:35
sigset.h
EV_TASK_INIT
#define EV_TASK_INIT(exec, func)
The static initializer for ev_task.
Definition: task.h:53
io_sigset_vtbl
Definition: sigset.h:63
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
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
slnode
A node in a singly-linked list.
Definition: sllist.h:40
io_sigset_wait::task
struct ev_task task
The task (to be) submitted upon completion (or cancellation) of the wait operation.
Definition: sigset.h:48
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_sigset_create
io_sigset_t * io_sigset_create(io_poll_t *poll, ev_exec_t *exec)
Creates a new system signal handler.
Definition: sigset.c:303
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