channel.scm 6.4 KB

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