channel-port.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
  3. ; Ports built on OS channels.
  4. ;----------------
  5. ; Records used as the PORT-DATA value in ports that read or write to channel.
  6. ; CLOSER is a function that closes the channel; socket channels have their own
  7. ; closing method.
  8. (define-synchronized-record-type channel-cell :channel-cell
  9. (really-make-channel-cell channel closer condvar in-use?)
  10. (in-use? sent)
  11. channel-cell?
  12. (channel channel-cell-ref)
  13. (closer channel-cell-closer)
  14. (condvar channel-cell-condvar)
  15. (in-use? channel-cell-in-use? set-channel-cell-in-use?!)
  16. (sent channel-cell-sent set-channel-cell-sent!))
  17. (define (make-channel-cell channel closer)
  18. (really-make-channel-cell channel closer (make-condvar) #f))
  19. ; Extracting the channel from a port.
  20. (define (port->channel port)
  21. (let ((data (port-data port)))
  22. (if (channel-cell? data)
  23. (channel-cell-ref data)
  24. #f)))
  25. ; Closing a port's channel. This is called with a proposal already in place.
  26. (define (port-channel-closer cell)
  27. (channel-maybe-commit-and-close (channel-cell-ref cell)
  28. (channel-cell-closer cell)))
  29. ;----------------
  30. ; Input ports
  31. ; Four possibilities:
  32. ; A. there is no read in progress
  33. ; -> initiate a read
  34. ; B. a read has completed
  35. ; -> update the port
  36. ; C. a read has been started and has not completed
  37. ; -> wait for it
  38. ; D. we don't want to wait
  39. ; -> so we don't
  40. (define (fill-buffer! port wait?)
  41. (let ((cell (port-data port))
  42. (buffer (port-buffer port)))
  43. (let ((condvar (channel-cell-condvar cell))
  44. (channel (channel-cell-ref cell)))
  45. (cond ((not (channel-cell-in-use? cell))
  46. (set-channel-cell-in-use?! cell #t)
  47. (let ((limit (provisional-port-limit port)))
  48. (channel-maybe-commit-and-read channel
  49. buffer
  50. limit
  51. (- (byte-vector-length buffer) limit)
  52. condvar
  53. wait?))
  54. #f) ; caller should retry as results may now be available
  55. ((condvar-has-value? condvar)
  56. (let ((result (condvar-value condvar)))
  57. (set-channel-cell-in-use?! cell #f)
  58. (set-condvar-has-value?! condvar #f)
  59. (note-buffer-reuse! port)
  60. (cond
  61. ((eof-object? result)
  62. (provisional-set-port-pending-eof?! port #t))
  63. ((i/o-error? result)
  64. (if (maybe-commit)
  65. (signal-condition result)
  66. #f))
  67. (else
  68. (provisional-set-port-limit! port
  69. (+ (provisional-port-limit port) result))))
  70. (maybe-commit)))
  71. (wait?
  72. (maybe-commit-and-wait-for-condvar condvar #f))
  73. (else
  74. (maybe-commit))))))
  75. (define (channel-port-ready? port)
  76. (let ((ready? (channel-ready? (channel-cell-ref (port-data port)))))
  77. (if (maybe-commit)
  78. (values #t ready?)
  79. (values #f #f))))
  80. (define input-channel-handler
  81. (make-buffered-input-port-handler
  82. (lambda (cell)
  83. (list 'input-port
  84. (channel-cell-ref cell)))
  85. port-channel-closer
  86. fill-buffer!
  87. channel-port-ready?))
  88. (define (input-channel->port channel . maybe-buffer-size)
  89. (real-input-channel->port channel maybe-buffer-size close-channel))
  90. ; This is for sockets, which have their own closing mechanism.
  91. (define (input-channel+closer->port channel closer . maybe-buffer-size)
  92. (real-input-channel->port channel maybe-buffer-size closer))
  93. (define (real-input-channel->port channel maybe-buffer-size closer)
  94. (let ((buffer-size (if (null? maybe-buffer-size)
  95. (channel-buffer-size)
  96. (car maybe-buffer-size))))
  97. (if (>= 0 buffer-size)
  98. (assertion-violation 'real-input-channel->port
  99. "invalid buffer size"
  100. input-channel->port channel buffer-size)
  101. (let ((port
  102. (make-buffered-input-port input-channel-handler
  103. (make-channel-cell channel closer)
  104. (make-byte-vector buffer-size 0)
  105. 0
  106. 0)))
  107. (set-port-crlf?! port (channel-crlf?))
  108. port))))
  109. ;----------------
  110. ; Output ports
  111. ; A. No write already in progress
  112. ; -> start one
  113. ; B. A write has completed
  114. ; -> if we're done then reset the index, otherwise write some more
  115. ; C. Wait.
  116. ;
  117. ; If NECESSARY? is #f we are doing a periodic buffer flushing and shouldn't
  118. ; bother to wait if someone else is already writing out the buffer.
  119. (define (empty-buffer! port necessary?)
  120. (let* ((cell (port-data port))
  121. (condvar (channel-cell-condvar cell)))
  122. (cond ((not (channel-cell-in-use? cell))
  123. (let ((buffer (port-buffer port))
  124. (count (provisional-port-index port)))
  125. (set-channel-cell-in-use?! cell #t)
  126. (send-some port 0 necessary?)))
  127. ((condvar-has-value? condvar)
  128. (let ((result (condvar-value condvar)))
  129. (set-condvar-has-value?! condvar #f)
  130. (if (i/o-error? result)
  131. (begin
  132. ;; #### We should probably maintain some kind of
  133. ;; "error status" with the channel cell that allows
  134. ;; actual recovery.
  135. ;; The way it is, we just pretend we're done so the
  136. ;; the periodic buffer flushing doesn't annoy the heck
  137. ;; out of us.
  138. (provisional-set-port-index! port 0)
  139. ;; good housekeeping; also keeps port-buffer flusher sane
  140. (provisional-set-port-pending-eof?! port #f)
  141. (note-buffer-reuse! port)
  142. (set-channel-cell-in-use?! cell #f)
  143. (if (maybe-commit)
  144. (signal-condition result)
  145. #f))
  146. (let ((sent (+ result (channel-cell-sent cell))))
  147. (if (< sent
  148. (provisional-port-index port))
  149. (send-some port sent necessary?)
  150. (begin
  151. (provisional-set-port-index! port 0)
  152. (note-buffer-reuse! port)
  153. (set-channel-cell-in-use?! cell #f)
  154. (maybe-commit)))))))
  155. (necessary?
  156. (maybe-commit-and-wait-for-condvar condvar #f))
  157. (else
  158. (maybe-commit)))))
  159. ; Try writing the rest of PORT's buffer. SENT bytes have already been
  160. ; written out.
  161. (define (send-some port sent wait?)
  162. (let ((cell (port-data port)))
  163. (set-channel-cell-sent! cell sent)
  164. (channel-maybe-commit-and-write (channel-cell-ref cell)
  165. (port-buffer port)
  166. sent
  167. (- (provisional-port-index port)
  168. sent)
  169. (channel-cell-condvar cell)
  170. wait?)))
  171. (define output-channel-handler
  172. (make-buffered-output-port-handler
  173. (lambda (cell)
  174. (list 'output-port
  175. (channel-cell-ref cell)))
  176. port-channel-closer
  177. empty-buffer!
  178. channel-port-ready?))
  179. (define (output-channel->port channel . maybe-buffer-size)
  180. (let ((port
  181. (if (and (not (null? maybe-buffer-size))
  182. (eq? 0 (car maybe-buffer-size)))
  183. (make-unbuffered-output-port unbuffered-output-handler
  184. (make-channel-cell channel close-channel))
  185. (real-output-channel->port channel maybe-buffer-size close-channel))))
  186. (set-port-crlf?! port (channel-crlf?))
  187. port))
  188. ; This is for sockets, which have their own closing mechanism.
  189. (define (output-channel+closer->port channel closer . maybe-buffer-size)
  190. (real-output-channel->port channel maybe-buffer-size closer))
  191. ; Dispatch on the buffer size to make the appropriate port. A buffer
  192. ; size of zero creates an unbuffered port. Buffered output ports get a
  193. ; finalizer to flush the buffer if the port is GC'ed.
  194. (define (real-output-channel->port channel maybe-buffer-size closer)
  195. (let ((buffer-size (if (null? maybe-buffer-size)
  196. (channel-buffer-size)
  197. (car maybe-buffer-size))))
  198. (if (or (not (integer? buffer-size))
  199. (< buffer-size 0)
  200. (not (channel? channel)))
  201. (assertion-violation 'real-output-channel->port
  202. "invalid argument"
  203. output-channel->port channel buffer-size)
  204. (let ((port (make-buffered-output-port output-channel-handler
  205. (make-channel-cell channel
  206. closer)
  207. (make-byte-vector buffer-size 0)
  208. 0
  209. buffer-size)))
  210. (periodically-force-output! port)
  211. (add-finalizer! port force-output-if-open)
  212. port))))
  213. ;----------------
  214. ; Various ways to open ports on files.
  215. ; First a generic procedure to do the work.
  216. (define (maybe-open-file op file-name option close-silently? coercion)
  217. (let ((thing
  218. (with-handler
  219. (lambda (c punt)
  220. (cond
  221. ((and (vm-exception? c)
  222. (eq? 'os-error
  223. (vm-exception-reason c)))
  224. (punt (condition
  225. (make-i/o-error)
  226. (make-who-condition op)
  227. (make-message-condition
  228. (os-string->string
  229. (byte-vector->os-string
  230. (os-error-message (car (reverse (condition-irritants c)))))))
  231. (make-irritants-condition (list file-name)))))
  232. (else
  233. (punt))))
  234. (lambda ()
  235. (let ((file-name/os (x->os-string file-name)))
  236. (open-channel (os-string->byte-vector file-name/os)
  237. (os-string->string file-name/os)
  238. option close-silently?))))))
  239. (coercion thing (channel-buffer-size))))
  240. ; And then all of RnRS's file opening procedures.
  241. (define (really-open-input-file op string close-silently?)
  242. (maybe-open-file op
  243. string
  244. (enum channel-status-option input)
  245. close-silently?
  246. input-channel->port))
  247. (define (open-input-file string)
  248. (really-open-input-file 'open-input-file string #f))
  249. (define (really-open-output-file op string close-silently?)
  250. (maybe-open-file op
  251. string
  252. (enum channel-status-option output)
  253. close-silently?
  254. output-channel->port))
  255. (define (open-output-file string)
  256. (really-open-output-file 'open-output-file string #f))
  257. (define (call-with-input-file string proc)
  258. (let* ((port (really-open-input-file 'call-with-input-file string #t))
  259. (results (call-with-values (lambda () (proc port))
  260. list)))
  261. (close-input-port port)
  262. (apply values results)))
  263. (define (call-with-output-file string proc)
  264. (let* ((port (really-open-output-file 'call-with-output-file string #t))
  265. (results (call-with-values (lambda () (proc port))
  266. list)))
  267. (close-output-port port)
  268. (apply values results)))
  269. (define (with-input-from-file string thunk)
  270. (call-with-input-file string
  271. (lambda (port)
  272. (call-with-current-input-port port thunk))))
  273. (define (with-output-to-file string thunk)
  274. (call-with-output-file string
  275. (lambda (port)
  276. (call-with-current-output-port port thunk))))
  277. ;----------------
  278. ; Flush the output buffers of all channel output ports. This is done before
  279. ; forking the current process.
  280. (define (force-channel-output-ports!)
  281. (for-each (lambda (port)
  282. (if (port->channel port)
  283. (force-output-if-open port)))
  284. (periodically-flushed-ports)))
  285. ;----------------
  286. ; Unbuffered output channel ports.
  287. ; This is used for the initial current-error-port.
  288. (define unbuffered-output-handler
  289. (make-unbuffered-output-port-handler (lambda (port)
  290. (list 'output-port
  291. (channel-cell-ref (port-data port))))
  292. (lambda (port)
  293. (port-channel-closer (port-data port)))
  294. (lambda (port buffer start count)
  295. (channel-write (channel-cell-ref (port-data port))
  296. buffer start count))
  297. (lambda (port) ; ready
  298. (channel-ready? (channel-cell-ref (port-data port))))))
  299. ; Utilities
  300. (define (channel-buffer-size)
  301. (channel-parameter (enum channel-parameter-option buffer-size)))
  302. (define (channel-crlf?)
  303. (channel-parameter (enum channel-parameter-option crlf?)))