epoll.scm 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. ;; epoll
  2. ;;;; Copyright (C) 2016 Andy Wingo <wingo@pobox.com>
  3. ;;;;
  4. ;;;; This library is free software; you can redistribute it and/or
  5. ;;;; modify it under the terms of the GNU Lesser General Public
  6. ;;;; License as published by the Free Software Foundation; either
  7. ;;;; version 3 of the License, or (at your option) any later version.
  8. ;;;;
  9. ;;;; This library is distributed in the hope that it will be useful,
  10. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;;; Lesser General Public License for more details.
  13. ;;;;
  14. ;;;; You should have received a copy of the GNU Lesser General Public
  15. ;;;; License along with this library; if not, write to the Free Software
  16. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. ;;;;
  18. (define-module (fibers epoll)
  19. #:use-module ((ice-9 binary-ports) #:select (get-u8 put-u8))
  20. #:use-module (ice-9 atomic)
  21. #:use-module (ice-9 control)
  22. #:use-module (ice-9 match)
  23. #:use-module (srfi srfi-9)
  24. #:use-module (srfi srfi-9 gnu)
  25. #:use-module (rnrs bytevectors)
  26. #:use-module (fibers config)
  27. #:export (epoll-create
  28. epoll-destroy
  29. epoll?
  30. epoll-add!
  31. epoll-modify!
  32. epoll-add*!
  33. epoll-remove!
  34. epoll-wake!
  35. epoll
  36. EPOLLIN EPOLLOUT EPOLLPRO EPOLLERR EPOLLHUP EPOLLET))
  37. (eval-when (eval load compile)
  38. (dynamic-call "init_fibers_epoll"
  39. (dynamic-link (extension-library "epoll"))))
  40. (when (defined? 'EPOLLRDHUP)
  41. (export EPOLLRDHUP))
  42. (when (defined? 'EPOLLONESHOT)
  43. (export EPOLLONESHOT))
  44. (define (make-wake-pipe)
  45. (define (set-nonblocking! port)
  46. (fcntl port F_SETFL (logior O_NONBLOCK (fcntl port F_GETFL))))
  47. (let ((pair (pipe)))
  48. (match pair
  49. ((read-pipe . write-pipe)
  50. (setvbuf write-pipe 'none)
  51. (set-nonblocking! read-pipe)
  52. (set-nonblocking! write-pipe)
  53. (values read-pipe write-pipe)))))
  54. (define-record-type <epoll>
  55. (make-epoll fd eventsv maxevents state wake-read-pipe wake-write-pipe)
  56. epoll?
  57. (fd epoll-fd set-epoll-fd!)
  58. (eventsv epoll-eventsv set-epoll-eventsv!)
  59. (maxevents epoll-maxevents set-epoll-maxevents!)
  60. ;; atomic box of either 'waiting, 'not-waiting or 'dead
  61. (state epoll-state)
  62. (wake-read-pipe epoll-wake-read-pipe)
  63. (wake-write-pipe epoll-wake-write-pipe))
  64. (define-syntax events-offset
  65. (lambda (x)
  66. (syntax-case x ()
  67. ((_ n)
  68. #`(* n #,%sizeof-struct-epoll-event)))))
  69. (define-syntax fd-offset
  70. (lambda (x)
  71. (syntax-case x ()
  72. ((_ n)
  73. #`(+ (* n #,%sizeof-struct-epoll-event)
  74. #,%offsetof-struct-epoll-event-fd)))))
  75. (define epoll-guardian (make-guardian))
  76. (define (pump-epoll-guardian)
  77. (let ((epoll (epoll-guardian)))
  78. (when epoll
  79. (epoll-destroy epoll)
  80. (pump-epoll-guardian))))
  81. (add-hook! after-gc-hook pump-epoll-guardian)
  82. (define* (epoll-create #:key (close-on-exec? #t) (maxevents 8))
  83. (call-with-values (lambda () (make-wake-pipe))
  84. (lambda (read-pipe write-pipe)
  85. (let* ((state (make-atomic-box 'not-waiting))
  86. (epoll (make-epoll (primitive-epoll-create close-on-exec?)
  87. #f maxevents state read-pipe write-pipe)))
  88. (epoll-guardian epoll)
  89. (epoll-add! epoll (fileno read-pipe) EPOLLIN)
  90. epoll))))
  91. (define (epoll-destroy epoll)
  92. (atomic-box-set! (epoll-state epoll) 'dead)
  93. (when (epoll-fd epoll)
  94. (close-port (epoll-wake-read-pipe epoll))
  95. ;; FIXME: ignore errors flushing output
  96. (close-port (epoll-wake-write-pipe epoll))
  97. (close-fdes (epoll-fd epoll))
  98. (set-epoll-fd! epoll #f)))
  99. (define (epoll-add! epoll fd events)
  100. (primitive-epoll-ctl (epoll-fd epoll) EPOLL_CTL_ADD fd events))
  101. (define (epoll-modify! epoll fd events)
  102. (primitive-epoll-ctl (epoll-fd epoll) EPOLL_CTL_MOD fd events))
  103. (define (epoll-add*! epoll fd events)
  104. (catch 'system-error
  105. (lambda () (epoll-modify! epoll fd events))
  106. (lambda _
  107. (epoll-add! epoll fd events))))
  108. (define (epoll-remove! epoll fd)
  109. (primitive-epoll-ctl (epoll-fd epoll) EPOLL_CTL_DEL fd))
  110. (define (epoll-wake! epoll)
  111. "Run after modifying the shared state used by a thread that might be
  112. waiting on this epoll descriptor, to break that thread out of the
  113. epoll wait (if appropriate)."
  114. (match (atomic-box-ref (epoll-state epoll))
  115. ;; It is always correct to wake an epoll via the pipe. However we
  116. ;; can avoid it if the epoll is guaranteed to see that the
  117. ;; runqueue is not empty before it goes to poll next time.
  118. ('waiting
  119. (primitive-epoll-wake (fileno (epoll-wake-write-pipe epoll))))
  120. ('not-waiting #t)
  121. ('dead (error "epoll instance is dead"))))
  122. (define (epoll-default-folder fd events seed)
  123. (acons fd events seed))
  124. (define (ensure-epoll-eventsv epoll maxevents)
  125. (let ((prev (epoll-eventsv epoll)))
  126. (if (and prev
  127. (or (not maxevents)
  128. (= (events-offset maxevents) (bytevector-length prev))))
  129. prev
  130. (let ((v (make-bytevector (events-offset (or maxevents 8)))))
  131. (set-epoll-eventsv! epoll v)
  132. v))))
  133. (define* (epoll epoll #:key (expiry #f)
  134. (update-expiry (lambda (expiry) expiry))
  135. (folder epoll-default-folder) (seed '()))
  136. (define (expiry->timeout expiry)
  137. (cond
  138. ((not expiry) -1)
  139. (else
  140. (let ((now (get-internal-real-time)))
  141. (cond
  142. ((< expiry now) 0)
  143. (else (- expiry now)))))))
  144. (let* ((maxevents (epoll-maxevents epoll))
  145. (eventsv (ensure-epoll-eventsv epoll maxevents))
  146. (write-pipe-fd (fileno (epoll-wake-write-pipe epoll)))
  147. (read-pipe-fd (fileno (epoll-wake-read-pipe epoll))))
  148. (atomic-box-set! (epoll-state epoll) 'waiting)
  149. ;; Note: update-expiry call must take place after epoll-state is
  150. ;; set to waiting.
  151. (let* ((timeout (expiry->timeout (update-expiry expiry)))
  152. (n (primitive-epoll-wait (epoll-fd epoll)
  153. write-pipe-fd read-pipe-fd
  154. eventsv timeout)))
  155. (atomic-box-set! (epoll-state epoll) 'not-waiting)
  156. ;; If we received `maxevents' events, it means that probably there
  157. ;; are more active fd's in the queue that we were unable to
  158. ;; receive. Expand our event buffer in that case.
  159. (when (= n maxevents)
  160. (set-epoll-maxevents! epoll (* maxevents 2)))
  161. (let lp ((seed seed) (i 0))
  162. (if (< i n)
  163. (let ((fd (bytevector-s32-native-ref eventsv (fd-offset i)))
  164. (events (bytevector-u32-native-ref eventsv (events-offset i))))
  165. (lp (folder fd events seed) (1+ i)))
  166. seed)))))