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:
parent
a04ba8aef3
commit
d253b89ba8
|
@ -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))
|
||||
|
|
|
@ -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?))
|
||||
|
||||
;; --------------------------------------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user