channel-port.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Ports built on OS channels.
  3. ;----------------
  4. ; Records used as the PORT-DATA value in ports that read or write to channel.
  5. ; CLOSER is a function that closes the channel; socket channels have their own
  6. ; closing method.
  7. (define-synchronized-record-type channel-cell :channel-cell
  8. (really-make-channel-cell channel closer condvar in-use?)
  9. (in-use? sent)
  10. channel-cell?
  11. (channel channel-cell-ref)
  12. (closer channel-cell-closer)
  13. (condvar channel-cell-condvar)
  14. (in-use? channel-cell-in-use? set-channel-cell-in-use?!)
  15. (sent channel-cell-sent set-channel-cell-sent!))
  16. (define (make-channel-cell channel closer)
  17. (really-make-channel-cell channel closer (make-condvar) #f))
  18. ; Extracting the channel from a port.
  19. (define (port->channel port)
  20. (let ((data (port-data port)))
  21. (if (channel-cell? data)
  22. (channel-cell-ref data)
  23. #f)))
  24. ; Closing a port's channel. This is called with a proposal already in place.
  25. (define (port-channel-closer cell)
  26. (channel-maybe-commit-and-close (channel-cell-ref cell)
  27. (channel-cell-closer cell)))
  28. ;----------------
  29. ; Input ports
  30. ; Four possibilities:
  31. ; A. there is no read in progress
  32. ; -> initiate a read
  33. ; B. a read has completed
  34. ; -> update the port
  35. ; C. a read has been started and has not completed
  36. ; -> wait for it
  37. ; D. we don't want to wait
  38. ; -> so we don't
  39. (define (fill-buffer! port wait?)
  40. (let ((cell (port-data port))
  41. (buffer (port-buffer port)))
  42. (let ((condvar (channel-cell-condvar cell))
  43. (channel (channel-cell-ref cell)))
  44. (cond ((not (channel-cell-in-use? cell))
  45. (set-channel-cell-in-use?! cell #t)
  46. (let ((limit (provisional-port-limit port)))
  47. (channel-maybe-commit-and-read channel
  48. buffer
  49. limit
  50. (- (byte-vector-length buffer) limit)
  51. condvar
  52. wait?))
  53. #f) ; caller should retry as results may now be available
  54. ((condvar-has-value? condvar)
  55. (let ((result (condvar-value condvar)))
  56. (set-channel-cell-in-use?! cell #f)
  57. (set-condvar-has-value?! condvar #f)
  58. (note-buffer-reuse! port)
  59. (cond
  60. ((eof-object? result)
  61. (provisional-set-port-pending-eof?! port #t))
  62. ((i/o-error? result)
  63. (if (maybe-commit)
  64. (signal-condition result)
  65. #f))
  66. (else
  67. (provisional-set-port-limit! port
  68. (+ (provisional-port-limit port) result))))
  69. (maybe-commit)))
  70. (wait?
  71. (maybe-commit-and-wait-for-condvar condvar))
  72. (else
  73. (maybe-commit))))))
  74. (define (channel-port-ready? port)
  75. (let ((ready? (channel-ready? (channel-cell-ref (port-data port)))))
  76. (if (maybe-commit)
  77. (values #t ready?)
  78. (values #f #f))))
  79. (define input-channel-handler
  80. (make-buffered-input-port-handler
  81. (lambda (cell)
  82. (list 'input-port
  83. (channel-cell-ref cell)))
  84. port-channel-closer
  85. fill-buffer!
  86. channel-port-ready?))
  87. (define (input-channel->port channel . maybe-buffer-size)
  88. (real-input-channel->port channel maybe-buffer-size close-channel))
  89. ; This is for sockets, which have their own closing mechanism.
  90. (define (input-channel+closer->port channel closer . maybe-buffer-size)
  91. (real-input-channel->port channel maybe-buffer-size closer))
  92. (define (real-input-channel->port channel maybe-buffer-size closer)
  93. (let ((buffer-size (if (null? maybe-buffer-size)
  94. (channel-buffer-size)
  95. (car maybe-buffer-size))))
  96. (if (>= 0 buffer-size)
  97. (call-error "invalid buffer size"
  98. input-channel->port channel buffer-size)
  99. (let ((port
  100. (make-buffered-input-port input-channel-handler
  101. (make-channel-cell channel closer)
  102. (make-byte-vector buffer-size 0)
  103. 0
  104. 0)))
  105. (set-port-crlf?! port (channel-crlf?))
  106. port))))
  107. ;----------------
  108. ; Output ports
  109. ; A. No write already in progress
  110. ; -> start one
  111. ; B. A write has completed
  112. ; -> if we're done then reset the index, otherwise write some more
  113. ; C. Wait.
  114. ;
  115. ; If NECESSARY? is #f we are doing a periodic buffer flushing and shouldn't
  116. ; bother to wait if someone else is already writing out the buffer.
  117. (define (empty-buffer! port necessary?)
  118. (let* ((cell (port-data port))
  119. (condvar (channel-cell-condvar cell)))
  120. (cond ((not (channel-cell-in-use? cell))
  121. (let ((buffer (port-buffer port))
  122. (count (provisional-port-index port)))
  123. (set-channel-cell-in-use?! cell #t)
  124. (send-some port 0 necessary?)))
  125. ((condvar-has-value? condvar)
  126. (let ((result (condvar-value condvar)))
  127. (set-condvar-has-value?! condvar #f)
  128. (if (i/o-error? result)
  129. (begin
  130. ;; #### We should probably maintain some kind of
  131. ;; "error status" with the channel cell that allows
  132. ;; actual recovery.
  133. ;; The way it is, we just pretend we're done so the
  134. ;; the periodic buffer flushing doesn't annoy the heck
  135. ;; out of us.
  136. (provisional-set-port-index! port 0)
  137. ;; good housekeeping; also keeps port-buffer flusher sane
  138. (provisional-set-port-pending-eof?! port #f)
  139. (note-buffer-reuse! port)
  140. (set-channel-cell-in-use?! cell #f)
  141. (if (maybe-commit)
  142. (signal-condition result)
  143. #f))
  144. (let ((sent (+ result (channel-cell-sent cell))))
  145. (if (< sent
  146. (provisional-port-index port))
  147. (send-some port sent necessary?)
  148. (begin
  149. (provisional-set-port-index! port 0)
  150. (note-buffer-reuse! port)
  151. (set-channel-cell-in-use?! cell #f)
  152. (maybe-commit)))))))
  153. (necessary?
  154. (maybe-commit-and-wait-for-condvar condvar))
  155. (else
  156. (maybe-commit)))))
  157. ; Try writing the rest of PORT's buffer. SENT bytes have already been
  158. ; written out.
  159. (define (send-some port sent wait?)
  160. (let ((cell (port-data port)))
  161. (set-channel-cell-sent! cell sent)
  162. (channel-maybe-commit-and-write (channel-cell-ref cell)
  163. (port-buffer port)
  164. sent
  165. (- (provisional-port-index port)
  166. sent)
  167. (channel-cell-condvar cell)
  168. wait?)))
  169. (define output-channel-handler
  170. (make-buffered-output-port-handler
  171. (lambda (cell)
  172. (list 'output-port
  173. (channel-cell-ref cell)))
  174. port-channel-closer
  175. empty-buffer!
  176. channel-port-ready?))
  177. (define (output-channel->port channel . maybe-buffer-size)
  178. (let ((port
  179. (if (and (not (null? maybe-buffer-size))
  180. (eq? 0 (car maybe-buffer-size)))
  181. (make-unbuffered-output-port unbuffered-output-handler
  182. (make-channel-cell channel close-channel))
  183. (real-output-channel->port channel maybe-buffer-size close-channel))))
  184. (set-port-crlf?! port (channel-crlf?))
  185. port))
  186. ; This is for sockets, which have their own closing mechanism.
  187. (define (output-channel+closer->port channel closer . maybe-buffer-size)
  188. (real-output-channel->port channel maybe-buffer-size closer))
  189. ; Dispatch on the buffer size to make the appropriate port. A buffer
  190. ; size of zero creates an unbuffered port. Buffered output ports get a
  191. ; finalizer to flush the buffer if the port is GC'ed.
  192. (define (real-output-channel->port channel maybe-buffer-size closer)
  193. (let ((buffer-size (if (null? maybe-buffer-size)
  194. (channel-buffer-size)
  195. (car maybe-buffer-size))))
  196. (if (or (not (integer? buffer-size))
  197. (< buffer-size 0)
  198. (not (channel? channel)))
  199. (call-error "invalid argument"
  200. output-channel->port channel buffer-size)
  201. (let ((port (make-buffered-output-port output-channel-handler
  202. (make-channel-cell channel
  203. closer)
  204. (make-byte-vector buffer-size 0)
  205. 0
  206. buffer-size)))
  207. (periodically-force-output! port)
  208. (add-finalizer! port force-output-if-open)
  209. port))))
  210. ;----------------
  211. ; Various ways to open ports on files.
  212. ; First a generic procedure to do the work.
  213. (define (maybe-open-file op file-name option close-silently? coercion)
  214. (let ((thing
  215. (with-handler
  216. (lambda (c punt)
  217. (cond
  218. ((and (vm-exception? c)
  219. (eq? 'os-error
  220. (vm-exception-reason c)))
  221. ;; We can't use PUNT here because the I/O error
  222. ;; condition object typically needs to be re-encoded,
  223. ;; and PUNT won't do that. So we exit regularly, and
  224. ;; call SIGNAL-CONDITION, which does the job. (If this
  225. ;; seems obscure to you, ask Mike.)
  226. (make-i/o-error
  227. (car (reverse (vm-exception-arguments c)))
  228. op
  229. (list file-name)))
  230. (else
  231. (punt))))
  232. (lambda ()
  233. (let ((file-name/os (x->os-string file-name)))
  234. (open-channel (os-string->byte-vector file-name/os)
  235. (os-string->string file-name/os)
  236. option close-silently?))))))
  237. (if (channel? thing)
  238. (coercion thing (channel-buffer-size))
  239. (signal-condition thing))))
  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?)))