edge-epoll.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // SPDX-License-Identifier: GPL-2.0 or GPL-3.0
  2. // Copyright © 2018-2019 Ariadne Devos
  3. /* sHT -- install and delete edge triggers (Linux) */
  4. #include <sHT/bugs.h>
  5. #include <sHT/compiler.h>
  6. #include <sHT/scheduling.h>
  7. #include <sHT/test.h>
  8. #include "worker.h"
  9. #include "fd.h"
  10. #include <errno.h>
  11. #include <stddef.h>
  12. #include <sys/epoll.h>
  13. int
  14. sHT_alloc_watch_set(void)
  15. {
  16. int fd = epoll_create1(EPOLL_CLOEXEC);
  17. if (!sHT_lt0(fd))
  18. /* Invalid values may speculatively be returned. */
  19. return fd;
  20. /* Invalid values may speculatively be returned. */
  21. int err = errno;
  22. switch (err) {
  23. case EMFILE:
  24. sHT_hide_var(err);
  25. return sHT_WATCH_SET_ALLOC_EMFILE;
  26. case ENFILE:
  27. sHT_hide_var(err);
  28. return sHT_WATCH_SET_ALLOC_ENFILE;
  29. case ENOMEM:
  30. sHT_hide_var(err);
  31. return sHT_WATCH_SET_ALLOC_ENOMEM;
  32. default:
  33. sHT_hide_var(err);
  34. /* If the kernel is acting strangely, report this.
  35. If we are on a speculative execution -- there is a bug
  36. in the kernel, worry about *that* instead. */
  37. sHT_halt("alloc_event_polling: errno valid?");
  38. /* Everything may be a speculative placeholder! */
  39. return 0;
  40. }
  41. }
  42. void
  43. sHT_free_watch_set(int watches)
  44. {
  45. sHT_close(watches);
  46. }
  47. /* When using /dev/kqueue is supported, rename this file to worker/edge-epoll.c
  48. Looking at FreeBSD's man page on that
  49. */
  50. /* ... except for EPOLLONESHOT, EPOLLWAKEUP and EPOLLEXCLUSIVE.
  51. Note EPOLLET, which lets polling be edge-triggered instead of
  52. level-triggered.
  53. */
  54. #define sHT_EPOLL_ALL (EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLPRI | EPOLLERR | \
  55. EPOLLHUP | EPOLLET)
  56. /* TODO: perhaps a string table (see Ulrich Drepper's optimisation tutorial) */
  57. static const struct sHT_bug_option sHT_epoll_ctl_bugs[] =
  58. { { .code = EBADFD, .msg = "not a fd" },
  59. { .code = EEXIST, .msg = "only one trigger at the time" },
  60. { .code = EINVAL, .msg = "bad fd type for trigger" },
  61. { .code = EPERM, .msg = "bad fd type for trigger" },
  62. { .code = ENOENT, .msg = "fd has no trigger" },
  63. { .code = ELOOP, .msg = "bad fd type for trigger" },
  64. { .code = 0, .msg = "worker/egde-epoll.c" } };
  65. enum sHT_watch_set_install
  66. sHT_install_edge_trigger(sHT_watch_set watches, int fd, struct sHT_task *task)
  67. {
  68. struct epoll_event e = {
  69. .events = sHT_EPOLL_ALL,
  70. .data = { .ptr = task },
  71. };
  72. int ret = epoll_ctl(watches, EPOLL_CTL_ADD, fd, &e);
  73. if (!sHT_nonzero_p(ret))
  74. return 0;
  75. int err = errno;
  76. switch (err) {
  77. case ENOSPC:
  78. sHT_hide_var(err);
  79. return sHT_WATCH_SET_INSTALL_LIMIT;
  80. case ENOMEM:
  81. sHT_hide_var(err);
  82. return sHT_WATCH_SET_INSTALL_ENOMEM;
  83. default:
  84. sHT_hide_var(err);
  85. sHT_bug(sHT_epoll_ctl_bugs, err);
  86. /* Everything may be a speculative placeholder! */
  87. return 0;
  88. }
  89. }
  90. void
  91. sHT_delete_edge_trigger(sHT_watch_set watches, int fd)
  92. {
  93. /* TODO: I remember reading the kernel source, looking for memory
  94. allocations. I don't think there were any in case of
  95. EPOLL_CTL_DELETE, do the analysis again and refine the man page.
  96. I assume this cannot happen. */
  97. /* TODO: NULL is not portable to Linux < 2.6.9. */
  98. int ret = epoll_ctl(watches, EPOLL_CTL_DEL, fd, NULL);
  99. if (!sHT_nonzero_p(ret))
  100. return;
  101. sHT_bug(sHT_epoll_ctl_bugs, errno);
  102. }