epoll.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /* Copyright (C) 2016 Andy Wingo <wingo@pobox.com>
  2. *
  3. * This library is free software; you can redistribute it and/or
  4. * modify it under the terms of the GNU Lesser General Public License
  5. * as published by the Free Software Foundation; either version 3 of
  6. * the License, or (at your option) any later version.
  7. *
  8. * This library is distributed in the hope that it will be useful, but
  9. * WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. * Lesser General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Lesser General Public
  14. * License along with this library; if not, write to the Free Software
  15. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  16. * 02110-1301 USA
  17. */
  18. #define _GNU_SOURCE
  19. #ifdef HAVE_CONFIG_H
  20. # include <config.h>
  21. #endif
  22. #include <errno.h>
  23. #include <unistd.h>
  24. #include <string.h>
  25. #include <sys/epoll.h>
  26. #include <libguile.h>
  27. /* {EPoll}
  28. */
  29. static SCM
  30. scm_primitive_epoll_wake (SCM wakefd)
  31. #define FUNC_NAME "primitive-epoll-wake"
  32. {
  33. int c_fd;
  34. char zero = 0;
  35. c_fd = scm_to_int (wakefd);
  36. if (write (c_fd, &zero, 1) <= 0 && errno != EWOULDBLOCK && errno != EAGAIN)
  37. SCM_SYSERROR;
  38. return SCM_UNSPECIFIED;
  39. }
  40. #undef FUNC_NAME
  41. /* EPoll is a newer Linux interface designed for sets of file
  42. descriptors that are mostly in a dormant state. These primitives
  43. wrap the epoll interface on a very low level.
  44. This is a low-level interface. See the `(fibers epoll)' module for
  45. a more usable wrapper. Note that this low-level interface deals in
  46. file descriptors, not ports, in order to allow higher-level code to
  47. handle the interaction with the garbage collector. */
  48. static SCM
  49. scm_primitive_epoll_create (SCM cloexec_p)
  50. #define FUNC_NAME "epoll-create"
  51. {
  52. int fd;
  53. #ifdef HAVE_EPOLL_CREATE1
  54. fd = epoll_create1 (scm_is_true (cloexec_p) ? EPOLL_CLOEXEC : 0);
  55. if (fd < 0)
  56. SCM_SYSERROR;
  57. #else
  58. fd = epoll_create (16);
  59. if (fd < 0)
  60. SCM_SYSERROR;
  61. if (scm_is_true (cloexec_p))
  62. fcntl (fd, F_SETFD, FD_CLOEXEC, 1);
  63. #endif
  64. return scm_from_int (fd);
  65. }
  66. #undef FUNC_NAME
  67. /* This epoll wrapper always places the fd itself as the "data" of the
  68. events structure. */
  69. static SCM
  70. scm_primitive_epoll_ctl (SCM epfd, SCM op, SCM fd, SCM events)
  71. #define FUNC_NAME "primitive-epoll-ctl"
  72. {
  73. int c_epfd, c_op, c_fd;
  74. struct epoll_event ev = { 0, };
  75. c_epfd = scm_to_int (epfd);
  76. c_op = scm_to_int (op);
  77. c_fd = scm_to_int (fd);
  78. if (SCM_UNBNDP (events))
  79. {
  80. if (c_op == EPOLL_CTL_DEL)
  81. /* Events do not matter in this case. */
  82. ev.events = 0;
  83. else
  84. SCM_MISC_ERROR ("missing events arg", SCM_EOL);
  85. }
  86. else
  87. ev.events = scm_to_uint32 (events);
  88. ev.data.fd = c_fd;
  89. if (epoll_ctl (c_epfd, c_op, c_fd, &ev))
  90. SCM_SYSERROR;
  91. return SCM_UNSPECIFIED;
  92. }
  93. #undef FUNC_NAME
  94. struct epoll_wait_data {
  95. int fd;
  96. struct epoll_event *events;
  97. int maxevents;
  98. int timeout;
  99. int rv;
  100. int err;
  101. };
  102. static void*
  103. do_epoll_wait (void *p)
  104. {
  105. struct epoll_wait_data *data = p;
  106. data->rv = epoll_wait (data->fd, data->events, data->maxevents,
  107. data->timeout);
  108. data->err = errno;
  109. return NULL;
  110. }
  111. static uint64_t time_units_per_millisecond;
  112. /* Wait on the files whose descriptors were registered on EPFD, and
  113. write the resulting events in EVENTSV, a bytevector. Returns the
  114. number of struct epoll_event values that were written to EVENTSV,
  115. which may be zero if no files triggered wakeups within TIMEOUT
  116. internal time units. */
  117. static SCM
  118. scm_primitive_epoll_wait (SCM epfd, SCM wakefd, SCM wokefd,
  119. SCM eventsv, SCM timeout)
  120. #define FUNC_NAME "primitive-epoll-wait"
  121. {
  122. int c_epfd, c_wakefd, c_wokefd, maxevents, rv, millis;
  123. int64_t c_timeout;
  124. struct epoll_event *events;
  125. c_epfd = scm_to_int (epfd);
  126. c_wakefd = scm_to_int (wakefd);
  127. c_wokefd = scm_to_int (wokefd);
  128. SCM_VALIDATE_BYTEVECTOR (SCM_ARG2, eventsv);
  129. if (SCM_UNLIKELY (SCM_BYTEVECTOR_LENGTH (eventsv) % sizeof (*events)))
  130. SCM_OUT_OF_RANGE (SCM_ARG2, eventsv);
  131. events = (struct epoll_event *) SCM_BYTEVECTOR_CONTENTS (eventsv);
  132. maxevents = SCM_BYTEVECTOR_LENGTH (eventsv) / sizeof (*events);
  133. c_timeout = scm_to_int64 (timeout);
  134. if (c_timeout < 0)
  135. millis = -1;
  136. else
  137. millis = c_timeout / time_units_per_millisecond;
  138. if (millis != 0 && scm_c_prepare_to_wait_on_fd (c_wakefd))
  139. rv = 0;
  140. else
  141. {
  142. struct epoll_wait_data data = { c_epfd, events, maxevents, millis };
  143. if (millis != 0)
  144. scm_without_guile (do_epoll_wait, &data);
  145. else
  146. do_epoll_wait (&data);
  147. rv = data.rv;
  148. if (millis != 0)
  149. scm_c_wait_finished ();
  150. if (rv < 0)
  151. {
  152. if (data.err == EINTR)
  153. rv = 0;
  154. else
  155. {
  156. errno = data.err;
  157. SCM_SYSERROR;
  158. }
  159. }
  160. else
  161. {
  162. /* Drain woke fd if appropriate. Doing it from Scheme is a
  163. bit gnarly as we don't know if suspendable ports are
  164. enabled or not. */
  165. int i;
  166. for (i = 0; i < rv; i++)
  167. if (events[i].data.fd == c_wokefd)
  168. {
  169. char zeroes[32];
  170. /* Remove wake fd from result set. */
  171. rv--;
  172. memmove (events + i,
  173. events + i + 1,
  174. (rv - i) * sizeof (*events));
  175. /* Drain fd and ignore errors. */
  176. while (read (c_wokefd, zeroes, sizeof zeroes) == sizeof zeroes)
  177. ;
  178. break;
  179. }
  180. }
  181. }
  182. return scm_from_int (rv);
  183. }
  184. #undef FUNC_NAME
  185. /* Low-level helpers for (fibers poll). */
  186. void
  187. init_fibers_epoll (void)
  188. {
  189. time_units_per_millisecond = scm_c_time_units_per_second / 1000;
  190. scm_c_define_gsubr ("primitive-epoll-wake", 1, 0, 0,
  191. scm_primitive_epoll_wake);
  192. scm_c_define_gsubr ("primitive-epoll-create", 1, 0, 0,
  193. scm_primitive_epoll_create);
  194. scm_c_define_gsubr ("primitive-epoll-ctl", 3, 1, 0,
  195. scm_primitive_epoll_ctl);
  196. scm_c_define_gsubr ("primitive-epoll-wait", 5, 0, 0,
  197. scm_primitive_epoll_wait);
  198. scm_c_define ("%sizeof-struct-epoll-event",
  199. scm_from_size_t (sizeof (struct epoll_event)));
  200. scm_c_define ("%offsetof-struct-epoll-event-fd",
  201. scm_from_size_t (offsetof (struct epoll_event, data.fd)));
  202. scm_c_define ("EPOLLIN", scm_from_int (EPOLLIN));
  203. scm_c_define ("EPOLLOUT", scm_from_int (EPOLLOUT));
  204. #ifdef EPOLLRDHUP
  205. scm_c_define ("EPOLLRDHUP", scm_from_int (EPOLLRDHUP));
  206. #endif
  207. scm_c_define ("EPOLLPRI", scm_from_int (EPOLLPRI));
  208. scm_c_define ("EPOLLERR", scm_from_int (EPOLLERR));
  209. scm_c_define ("EPOLLHUP", scm_from_int (EPOLLHUP));
  210. scm_c_define ("EPOLLET", scm_from_int (EPOLLET));
  211. #ifdef EPOLLONESHOT
  212. scm_c_define ("EPOLLONESHOT", scm_from_int (EPOLLONESHOT));
  213. #endif
  214. scm_c_define ("EPOLL_CTL_ADD", scm_from_int (EPOLL_CTL_ADD));
  215. scm_c_define ("EPOLL_CTL_MOD", scm_from_int (EPOLL_CTL_MOD));
  216. scm_c_define ("EPOLL_CTL_DEL", scm_from_int (EPOLL_CTL_DEL));
  217. }
  218. /*
  219. Local Variables:
  220. c-file-style: "gnu"
  221. End:
  222. */