io-wakeup.scm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. ;; Fibers: cooperative, event-driven user-space threads.
  2. ;;;; Copyright (C) 2016 Free Software Foundation, Inc.
  3. ;;;; Copyright (C) 2021 Maxime Devos
  4. ;;;;
  5. ;;;; This library is free software; you can redistribute it and/or
  6. ;;;; modify it under the terms of the GNU Lesser General Public
  7. ;;;; License as published by the Free Software Foundation; either
  8. ;;;; version 3 of the License, or (at your option) any later version.
  9. ;;;;
  10. ;;;; This library is distributed in the hope that it will be useful,
  11. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;;;; Lesser General Public License for more details.
  14. ;;;;
  15. ;;;; You should have received a copy of the GNU Lesser General Public
  16. ;;;; License along with this library; if not, write to the Free Software
  17. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. ;;;;
  19. (define-module (tests io-wakeup)
  20. #:use-module (rnrs bytevectors)
  21. #:use-module (ice-9 control)
  22. #:use-module (ice-9 suspendable-ports)
  23. #:use-module (ice-9 binary-ports)
  24. #:use-module (fibers)
  25. #:use-module (fibers io-wakeup)
  26. #:use-module (fibers operations)
  27. #:use-module (fibers timers))
  28. (define failed? #f)
  29. (define-syntax-rule (assert-equal expected actual)
  30. (let ((x expected))
  31. (format #t "assert ~s equal to ~s: " 'actual x)
  32. (force-output)
  33. (let ((y actual))
  34. (cond
  35. ((equal? x y) (format #t "ok\n"))
  36. (else
  37. (format #t "no (got ~s)\n" y)
  38. (set! failed? #t))))))
  39. (define-syntax-rule (assert-run-fibers-terminates exp)
  40. (begin
  41. (format #t "assert run-fibers on ~s terminates: " 'exp)
  42. (force-output)
  43. (let ((start (get-internal-real-time)))
  44. (call-with-values (lambda () (run-fibers (lambda () exp)))
  45. (lambda vals
  46. (format #t "ok (~a s)\n" (/ (- (get-internal-real-time) start)
  47. 1.0 internal-time-units-per-second))
  48. (apply values vals))))))
  49. (define-syntax-rule (assert-run-fibers-returns (expected ...) exp)
  50. (begin
  51. (call-with-values (lambda () (assert-run-fibers-terminates exp))
  52. (lambda run-fiber-return-vals
  53. (assert-equal '(expected ...) run-fiber-return-vals)))))
  54. ;; Note that theoretically, on very slow systems, SECONDS might need
  55. ;; to be increased. However, readable/timeout? and writable/timeout?
  56. ;; call this 5 times in a loop anyways, so the effective timeout is
  57. ;; a fourth of a second, which should be plenty in practice.
  58. (define* (with-timeout op #:key (seconds 0.05) (wrap values))
  59. (choice-operation op
  60. (wrap-operation (sleep-operation seconds) wrap)))
  61. (define* (readable/timeout? port #:key (allowed-spurious 5))
  62. "Does waiting for readability time-out?
  63. Allow @var{allowed-spurious} spurious wakeups."
  64. (or (perform-operation
  65. (with-timeout
  66. (wrap-operation (wait-until-port-readable-operation port)
  67. (lambda () #f))
  68. #:wrap (lambda () #t)))
  69. (and (> allowed-spurious 0)
  70. (readable/timeout? port #:allowed-spurious
  71. (- allowed-spurious 1)))))
  72. (define* (writable/timeout? port #:key (allowed-spurious 5))
  73. "Does waiting for writability time-out?
  74. Allow @var{allowed-spurious} spurious wakeups."
  75. (or (perform-operation
  76. (with-timeout
  77. (wrap-operation (wait-until-port-writable-operation port)
  78. (lambda () #f))
  79. #:wrap (lambda () #t)))
  80. (and (> allowed-spurious 0)
  81. (writable/timeout? port #:allowed-spurious
  82. (- allowed-spurious 1)))))
  83. ;; Tests:
  84. ;; * wait-until-port-readable-operaton / wait-until-port-writable-operation
  85. ;; blocks if the port isn't ready for input / output.
  86. ;;
  87. ;; This is tested with a pipe (read & write)
  88. ;; and a listening socket (read, or accept in this case).
  89. ;;
  90. ;; Due to the possibility of spurious wakeups,
  91. ;; a limited few spurious wakeups are tolerated.
  92. ;;
  93. ;; * these operations succeed if the port is ready for input / output.
  94. ;;
  95. ;; These are again tested with a pipe and a listening socket
  96. ;;
  97. ;; Blocking is detected with a small time-out.
  98. (define (make-listening-socket)
  99. (let ((server (socket PF_INET SOCK_DGRAM 0)))
  100. (bind server AF_INET INADDR_LOOPBACK 0)
  101. server))
  102. (let ((s (make-listening-socket)))
  103. (assert-run-fibers-returns (#t)
  104. (readable/timeout? s))
  105. (assert-equal #t (readable/timeout? s))
  106. (close s))
  107. (define (set-nonblocking! sock)
  108. (let ((flags (fcntl sock F_GETFL)))
  109. (fcntl sock F_SETFL (logior O_NONBLOCK flags))))
  110. (define-syntax-rule (with-pipes (A B) exp exp* ...)
  111. (let* ((pipes (pipe))
  112. (A (car pipes))
  113. (B (cdr pipes)))
  114. exp exp* ...
  115. (close A)
  116. (close B)))
  117. (with-pipes (A B)
  118. (setvbuf A 'none)
  119. (setvbuf B 'none)
  120. (assert-run-fibers-returns (#t)
  121. (readable/timeout? A))
  122. (assert-equal #t (readable/timeout? A))
  123. ;; The buffer is empty, so writability is expected.
  124. (assert-run-fibers-returns (#f)
  125. (writable/timeout? B))
  126. (assert-equal #f (writable/timeout? B))
  127. ;; Fill the buffer
  128. (set-nonblocking! B)
  129. (let ((bv (make-bytevector 1024)))
  130. (let/ec k
  131. (parameterize ((current-write-waiter k))
  132. (let loop ()
  133. (put-bytevector B bv)
  134. (loop)))))
  135. ;; As the buffer is full, writable/timeout? should return
  136. ;; #t.
  137. (assert-run-fibers-returns (#t)
  138. (writable/timeout? B))
  139. ;; There's plenty to read now, so readable/timeout? should
  140. ;; return #f.
  141. (assert-run-fibers-returns (#f)
  142. (readable/timeout? A)))
  143. (exit (if failed? 1 0))
  144. ;; Local Variables:
  145. ;; eval: (put 'with-pipes 'scheme-indent-function 1)
  146. ;; End: