channel.scm 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
  3. ; Channel interrupt stuff.
  4. ; Block reads and writes in terms of partial reads and writes.
  5. ; CHANNEL-READ returns the number of characters read or the EOF object.
  6. ; BUFFER is either a string or byte vector and START is the index at which
  7. ; to place the first character read. COUNT is the maximum number of characters
  8. ; that may be read. If WAIT? is true the thread should block if nothing
  9. ; is immediately available.
  10. ; We disable interrupts to be sure that we have installed CONDVAR before
  11. ; any completion interrupt arrives.
  12. ;
  13. ; Like all maybe-commits, this returns #T if it successfully committed and
  14. ; #F if the commit failed.
  15. (define (channel-maybe-commit-and-read channel buffer start count condvar wait?)
  16. (maybe-commit-no-interrupts
  17. (lambda ()
  18. (let ((got (channel-maybe-read channel buffer start count wait?)))
  19. (cond
  20. ((not got)
  21. (add-channel-condvar! channel condvar))
  22. ((cell? got)
  23. (note-channel-result! condvar
  24. (make-read/write-i/o-error 'channel-maybe-read
  25. (cell-ref got)
  26. channel buffer start count wait?)))
  27. (else
  28. (note-channel-result! condvar got)))))))
  29. (define (channel-maybe-commit-and-write channel buffer start count condvar wait?)
  30. (maybe-commit-no-interrupts
  31. (lambda ()
  32. (let ((got (channel-maybe-write channel buffer start count)))
  33. (cond
  34. ((not got)
  35. (add-channel-condvar! channel condvar)
  36. (if wait?
  37. (with-new-proposal (lose)
  38. (maybe-commit-and-wait-for-condvar condvar #f))))
  39. ((cell? got)
  40. (note-channel-result! condvar
  41. (make-read/write-i/o-error 'channel-maybe-write
  42. (cell-ref got)
  43. channel buffer start count wait?)))
  44. (else
  45. (note-channel-result! condvar got)))))))
  46. (define (make-read/write-i/o-error who status channel buffer start count wait?)
  47. (condition
  48. (make-i/o-error)
  49. (make-os-error status)
  50. (make-who-condition who)
  51. (make-message-condition
  52. (os-string->string
  53. (byte-vector->os-string
  54. (os-error-message status))))
  55. (make-irritants-condition (list channel buffer start count wait?))))
  56. ; Set CONDVAR's value to be RESULT.
  57. (define (note-channel-result! condvar result)
  58. (with-new-proposal (lose)
  59. (or (maybe-commit-and-set-condvar! condvar result)
  60. (lose))))
  61. ; Used for stderr, which is unbuffered both here and in C.
  62. (define (channel-write channel buffer start count)
  63. (let ((ints (disable-interrupts!)))
  64. (let ((res (channel-maybe-write channel buffer start count)))
  65. (if res
  66. (begin
  67. (set-enabled-interrupts! ints)
  68. res)
  69. (let ((condvar (make-condvar)))
  70. (add-channel-condvar! channel condvar)
  71. (with-new-proposal (lose)
  72. (or (maybe-commit-and-wait-for-condvar condvar #f)
  73. (lose)))
  74. (set-enabled-interrupts! ints)
  75. (condvar-value condvar))))))
  76. ;----------------
  77. (define (channel-maybe-commit-and-close channel close-channel)
  78. (maybe-commit-no-interrupts
  79. (lambda ()
  80. (let ((condvar (fetch-channel-condvar! channel)))
  81. (if condvar
  82. (begin
  83. (channel-abort channel)
  84. (close-channel channel)
  85. (note-channel-result! condvar
  86. (if (input-channel? channel)
  87. (eof-object)
  88. 0)))
  89. (close-channel channel))))))
  90. (define (input-channel? channel)
  91. (= (channel-status channel)
  92. (enum channel-status-option input)))
  93. ;----------------
  94. ; Install an interrupt handler that queues up the results of completed I/O
  95. ; operations and spawn a thread to cope with them. This is written so as
  96. ; to avoid having state in top-level variables, because their values are
  97. ; saved in dumped images.
  98. (define (initialize-channel-i/o!)
  99. (session-data-set! channel-wait-condvars-slot '())
  100. (set-interrupt-handler! (enum interrupt i/o-completion)
  101. i/o-completion-handler))
  102. ; The warning message is printed using DEBUG-MESSAGE because to try to make
  103. ; sure it appears in spite of whatever problem's the I/O system is having.
  104. ;
  105. ; Called with interrupts disabled.
  106. (define (i/o-completion-handler channel error? status enabled-interrupts)
  107. (let ((condvar (fetch-channel-condvar! channel)))
  108. (if condvar
  109. (note-channel-result! condvar
  110. (if error?
  111. (condition
  112. (make-i/o-error)
  113. (make-who-condition 'i/o-completion-handler)
  114. (make-message-condition (os-error-message status))
  115. (make-irritants-condition (list channel enabled-interrupts)))
  116. status)))))
  117. ; Exported procedure
  118. (define (zap-i/o-orphans!)
  119. (abort-unwanted-reads!)
  120. (not (null? (channel-condvars))))
  121. ;----------------
  122. ; A session slot contains an alist mapping channels to condvars for the result
  123. ; of an i/o operation on that channel.
  124. (define channel-wait-condvars-slot
  125. (make-session-data-slot! '()))
  126. (define (channel-condvars)
  127. (session-data-ref channel-wait-condvars-slot))
  128. (define (set-channel-condvars! condvars)
  129. (session-data-set! channel-wait-condvars-slot condvars))
  130. ; Adding a condvar and channel - the caller has already determined there is no
  131. ; existing condvar for this channel.
  132. (define (add-channel-condvar! channel condvar)
  133. (set-channel-condvars! (cons (cons channel condvar)
  134. (channel-condvars))))
  135. ; Exported interface.
  136. (define wait-for-channel add-channel-condvar!)
  137. ; This just deletes from the alist.
  138. (define (fetch-channel-condvar! channel)
  139. (let ((condvars (channel-condvars)))
  140. (cond ((null? condvars)
  141. #f)
  142. ((eq? channel (caar condvars))
  143. (set-channel-condvars! (cdr condvars))
  144. (cdar condvars))
  145. (else
  146. (let loop ((condvars (cdr condvars)) (prev condvars))
  147. (cond ((null? condvars)
  148. #f)
  149. ((eq? channel (caar condvars))
  150. (set-cdr! prev (cdr condvars))
  151. (cdar condvars))
  152. (else
  153. (loop (cdr condvars) condvars))))))))
  154. ; Abort the read operations for any channel whose condvar no longer has waiters.
  155. ; The main purpose of ABORT-UNWANTED-READS is to abort reads after the
  156. ; reading threads have died. The Scheme process sticks around until
  157. ; all I/O has been completed and there is no point in waiting for a
  158. ; read if no one wants the result.
  159. ; One upon a time, the intention was to have this procedure abort
  160. ; unwanted writes as well. However, we must not abort writes which
  161. ; come from the automatic buffer flushing routine, which is hard to
  162. ; detect here. Moreover, the automatic buffer flushing is currently
  163. ; hard to abort.
  164. (define (abort-unwanted-reads!)
  165. (let ((ints (disable-interrupts!)))
  166. (let loop ((condvars (channel-condvars)) (okay '()))
  167. (if (null? condvars)
  168. (begin
  169. (set-channel-condvars! okay)
  170. (set-enabled-interrupts! ints))
  171. (let ((condvar (cdar condvars)))
  172. (loop (cdr condvars)
  173. (if (or (not (input-channel? (caar condvars)))
  174. (condvar-has-waiters? condvar))
  175. (cons (car condvars) okay)
  176. (begin
  177. (channel-abort (caar condvars))
  178. (note-channel-result! condvar 0)
  179. okay))))))))