racket/port: fix read-bytes-evt' and read-string-evt'

As the documentation says, it's supposed to be ok to use the
same evt multiple times or in multiple threads, but an internal
buffer was allocated incorrectly, so that multiple/concurrent
uses could go wrong.

Closes PR 12860
This commit is contained in:
Matthew Flatt 2012-06-22 06:56:49 +08:00
parent a04ba8aef3
commit d253b89ba8
2 changed files with 102 additions and 45 deletions

View File

@ -1032,13 +1032,15 @@
(-read-bytes!-evt bstr input-port peek-offset prog-evt))
(define (-read-bytes-evt len input-port peek-offset prog-evt)
(let ([bstr (make-bytes len)])
(wrap-evt
(-read-bytes!-evt bstr input-port peek-offset prog-evt)
(lambda (v)
(if (number? v)
(if (= v len) bstr (subbytes bstr 0 v))
v)))))
(guard-evt
(lambda ()
(let ([bstr (make-bytes len)])
(wrap-evt
(-read-bytes!-evt bstr input-port peek-offset prog-evt)
(lambda (v)
(if (number? v)
(if (= v len) bstr (subbytes bstr 0 v))
v)))))))
(define (read-bytes-evt len input-port)
(-read-bytes-evt len input-port #f #f))
@ -1049,44 +1051,46 @@
(define (-read-string-evt goal input-port peek-offset prog-evt)
(if (zero? goal)
(wrap-evt always-evt (lambda (x) ""))
(let ([bstr (make-bytes goal)]
[c (bytes-open-converter "UTF-8-permissive" "UTF-8")])
(wrap-evt
(read-at-least-bytes!-evt
bstr input-port
(lambda (bstr v)
(if (= v (bytes-length bstr))
;; We can't easily use bytes-utf-8-length here,
;; because we may need more bytes to figure out
;; the true role of the last byte. The
;; `bytes-convert' function lets us deal with
;; the last byte properly.
(let-values ([(bstr2 used status)
(bytes-convert c bstr 0 v)])
(let ([got (bytes-utf-8-length bstr2)])
(if (= got goal)
;; Done:
#f
;; Need more bytes:
(let ([bstr2 (make-bytes (+ v (- goal got)))])
(bytes-copy! bstr2 0 bstr)
bstr2))))
;; Need more bytes in bstr:
bstr))
(lambda (bstr v)
;; We may need one less than v,
;; because we may have had to peek
;; an extra byte to discover an
;; error in the stream.
(if ((bytes-utf-8-length bstr #\? 0 v) . > . goal) (sub1 v) v))
cons
peek-offset prog-evt)
(lambda (bstr+v)
(let ([bstr (car bstr+v)]
[v (cdr bstr+v)])
(if (number? v)
(bytes->string/utf-8 bstr #\? 0 v)
v)))))))
(guard-evt
(lambda ()
(let ([bstr (make-bytes goal)]
[c (bytes-open-converter "UTF-8-permissive" "UTF-8")])
(wrap-evt
(read-at-least-bytes!-evt
bstr input-port
(lambda (bstr v)
(if (= v (bytes-length bstr))
;; We can't easily use bytes-utf-8-length here,
;; because we may need more bytes to figure out
;; the true role of the last byte. The
;; `bytes-convert' function lets us deal with
;; the last byte properly.
(let-values ([(bstr2 used status)
(bytes-convert c bstr 0 v)])
(let ([got (bytes-utf-8-length bstr2)])
(if (= got goal)
;; Done:
#f
;; Need more bytes:
(let ([bstr2 (make-bytes (+ v (- goal got)))])
(bytes-copy! bstr2 0 bstr)
bstr2))))
;; Need more bytes in bstr:
bstr))
(lambda (bstr v)
;; We may need one less than v,
;; because we may have had to peek
;; an extra byte to discover an
;; error in the stream.
(if ((bytes-utf-8-length bstr #\? 0 v) . > . goal) (sub1 v) v))
cons
peek-offset prog-evt)
(lambda (bstr+v)
(let ([bstr (car bstr+v)]
[v (cdr bstr+v)])
(if (number? v)
(bytes->string/utf-8 bstr #\? 0 v)
v)))))))))
(define (read-string-evt goal input-port)
(-read-string-evt goal input-port #f #f))

View File

@ -893,6 +893,59 @@
(flush-output out)
(test "hello world" read in))
;; --------------------------------------------------
;; check that string and byte-string evts can be reused
(let ()
(define (check-can-reuse read-bytes-evt read-bytes write-bytes integer->byte list->bytes bytes?)
(define N 10)
(define M 160)
(define PORT 5999)
(define (make-alarm-e)
(alarm-evt (+ (current-inexact-milliseconds) 5)))
(define ((connection-handler in out with-alarm?))
(let loop ((alarm-e (make-alarm-e))
(read-e (read-bytes-evt 16 in)))
(sync (if with-alarm?
(wrap-evt alarm-e (lambda (_) (loop (make-alarm-e) read-e)))
never-evt)
(wrap-evt read-e
(lambda (bs)
(when (bytes? bs)
(sleep 0.01)
(write-bytes bs out)
(flush-output out))
(loop alarm-e read-e)))
(wrap-evt (eof-evt in)
(lambda (_)
(close-input-port in)
(close-output-port out))))))
(define listener (tcp-listen PORT 4 #t))
(define server
(thread
(lambda ()
(for ([i N])
(define-values (in out) (tcp-accept listener))
((connection-handler in out #t))))))
(let ([s (list->bytes
(for/list ([i M])
(integer->byte (random 512))))])
(for ([i N])
(define-values (i o) (tcp-connect "localhost" PORT))
(write-bytes s o)
(close-output-port o)
(test s read-bytes M i)))
(sync server)
(tcp-close listener))
(let ([integer->byte (lambda (s) (bitwise-and s #xFF))])
(check-can-reuse read-bytes-evt read-bytes write-bytes integer->byte list->bytes bytes?))
(check-can-reuse read-string-evt read-string write-string integer->char list->string string?))
;; --------------------------------------------------