@@ -44,13 +44,16 @@ abstract type LibuvStream <: IO end
44
44
bytesavailable (s:: LibuvStream ) = bytesavailable (s. buffer)
45
45
46
46
function eof (s:: LibuvStream )
47
- if isopen (s) # fast path
48
- bytesavailable (s) <= 0 || return false
49
- else
50
- return bytesavailable (s) <= 0
51
- end
47
+ bytesavailable (s) > 0 && return false
52
48
wait_readnb (s, 1 )
53
- return ! isopen (s) && bytesavailable (s) <= 0
49
+ # This function is race-y if used from multiple threads, but we guarantee
50
+ # it to never return false until the stream is definitively exhausted
51
+ # and that we won't return true if there's a readerror pending (it'll instead get thrown).
52
+ # This requires some careful ordering here (TODO : atomic loads)
53
+ bytesavailable (s) > 0 && return false
54
+ open = isopen (s) # must preceed readerror check
55
+ s. readerror === nothing || throw (s. readerror)
56
+ return ! open
54
57
end
55
58
56
59
# Limit our default maximum read and buffer size,
@@ -327,17 +330,25 @@ function check_open(x::Union{LibuvStream, LibuvServer})
327
330
end
328
331
329
332
function wait_readnb (x:: LibuvStream , nb:: Int )
333
+ # fast path before iolock acquire
334
+ bytesavailable (x. buffer) >= nb && return
335
+ open = isopen (x) # must preceed readerror check
336
+ x. readerror === nothing || throw (x. readerror)
337
+ open || return
330
338
iolock_begin ()
331
- if ! isopen (x) || bytesavailable (x. buffer) >= nb # fast path
332
- iolock_end ()
333
- return
334
- end
339
+ # repeat fast path after iolock acquire, before other expensive work
340
+ bytesavailable (x. buffer) >= nb && (iolock_end (); return )
341
+ open = isopen (x)
342
+ x. readerror === nothing || throw (x. readerror)
343
+ open || (iolock_end (); return )
344
+ # now do the "real" work
335
345
oldthrottle = x. throttle
336
346
preserve_handle (x)
337
347
lock (x. cond)
338
348
try
339
- while isopen (x) && bytesavailable (x. buffer) < nb
349
+ while bytesavailable (x. buffer) < nb
340
350
x. readerror === nothing || throw (x. readerror)
351
+ isopen (x) || break
341
352
x. throttle = max (nb, x. throttle)
342
353
start_reading (x) # ensure we are reading
343
354
iolock_end ()
@@ -351,6 +362,8 @@ function wait_readnb(x::LibuvStream, nb::Int)
351
362
stop_reading (x) # stop reading iff there are currently no other read clients of the stream
352
363
end
353
364
if oldthrottle <= x. throttle <= nb
365
+ # if we're interleaving readers, we might not get back to the "original" throttle
366
+ # but we consider that an acceptable "risk", since we can't be quite sure what the intended value is now
354
367
x. throttle = oldthrottle
355
368
end
356
369
unpreserve_handle (x)
@@ -543,7 +556,6 @@ function uv_readcb(handle::Ptr{Cvoid}, nread::Cssize_t, buf::Ptr{Cvoid})
543
556
# remind the client that stream.buffer is full
544
557
notify (stream. cond)
545
558
elseif nread == UV_EOF
546
- stream. readerror = EOFError ()
547
559
if isa (stream, TTY)
548
560
stream. status = StatusEOF # libuv called uv_stop_reading already
549
561
notify (stream. cond)
@@ -589,7 +601,6 @@ function reseteof(x::TTY)
589
601
iolock_begin ()
590
602
if x. status == StatusEOF
591
603
x. status = StatusOpen
592
- x. readerror isa EOFError && (x. readerror = nothing )
593
604
end
594
605
iolock_end ()
595
606
nothing
@@ -772,40 +783,33 @@ function readbytes!(s::LibuvStream, a::Vector{UInt8}, nb::Int)
772
783
@assert sbuf. seekable == false
773
784
@assert sbuf. maxsize >= nb
774
785
775
- local nread
776
- if bytesavailable (sbuf) >= nb
777
- nread = readbytes! (sbuf, a, nb)
778
- iolock_end ()
779
- return nread
780
- end
781
-
782
- if nb <= SZ_UNBUFFERED_IO # Under this limit we are OK with copying the array from the stream's buffer
783
- while isopen (s) && bytesavailable (sbuf) < nb
786
+ function wait_locked (s, buf, nb)
787
+ while bytesavailable (buf) < nb
788
+ s. readerror === nothing || throw (s. readerror)
789
+ isopen (s) || break
784
790
iolock_end ()
785
791
wait_readnb (s, nb)
786
792
iolock_begin ()
787
793
end
788
- nread = readbytes! (sbuf, a, nb)
789
- iolock_end ()
790
- return nread
791
794
end
792
795
793
- nread = try
794
- stop_reading (s) # Just playing it safe, since we are going to switch buffers.
795
- newbuf = PipeBuffer (a, maxsize = nb)
796
+ if nb <= SZ_UNBUFFERED_IO # Under this limit we are OK with copying the array from the stream's buffer
797
+ wait_locked (s, sbuf, nb)
798
+ end
799
+ if bytesavailable (sbuf) >= nb
800
+ nread = readbytes! (sbuf, a, nb)
801
+ else
802
+ newbuf = PipeBuffer (a, maxsize= nb)
796
803
newbuf. size = 0 # reset the write pointer to the beginning
797
- s. buffer = newbuf
798
- write (newbuf, sbuf)
799
- iolock_end ()
800
- wait_readnb (s, Int (nb))
801
- iolock_begin ()
802
- compact (newbuf)
803
- bytesavailable (newbuf)
804
- finally
805
- s. buffer = sbuf
806
- if ! isempty (s. cond)
807
- start_reading (s) # resume reading iff there are currently other read clients of the stream
804
+ nread = try
805
+ s. buffer = newbuf
806
+ write (newbuf, sbuf)
807
+ wait_locked (s, newbuf, nb)
808
+ bytesavailable (newbuf)
809
+ finally
810
+ s. buffer = sbuf
808
811
end
812
+ compact (newbuf)
809
813
end
810
814
iolock_end ()
811
815
return nread
@@ -825,31 +829,30 @@ function unsafe_read(s::LibuvStream, p::Ptr{UInt8}, nb::UInt)
825
829
@assert sbuf. seekable == false
826
830
@assert sbuf. maxsize >= nb
827
831
828
- if bytesavailable (sbuf) >= nb
829
- unsafe_read (sbuf, p, nb)
830
- elseif nb <= SZ_UNBUFFERED_IO # Under this limit we are OK with copying the array from the stream's buffer
831
- while isopen (s) && bytesavailable (sbuf) < nb
832
+ function wait_locked (s, buf, nb)
833
+ while bytesavailable (buf) < nb
834
+ s . readerror === nothing || throw (s . readerror)
835
+ isopen (s) || throw ( EOFError ())
832
836
iolock_end ()
833
- wait_readnb (s, Int (nb) )
837
+ wait_readnb (s, nb )
834
838
iolock_begin ()
835
839
end
840
+ end
841
+
842
+ if nb <= SZ_UNBUFFERED_IO # Under this limit we are OK with copying the array from the stream's buffer
843
+ wait_locked (s, sbuf, Int (nb))
844
+ end
845
+ if bytesavailable (sbuf) >= nb
836
846
unsafe_read (sbuf, p, nb)
837
847
else
848
+ newbuf = PipeBuffer (unsafe_wrap (Array, p, nb), maxsize= Int (nb))
849
+ newbuf. size = 0 # reset the write pointer to the beginning
838
850
try
839
- stop_reading (s) # Just playing it safe, since we are going to switch buffers.
840
- newbuf = PipeBuffer (unsafe_wrap (Array, p, nb), maxsize = Int (nb))
841
- newbuf. size = 0 # reset the write pointer to the beginning
842
851
s. buffer = newbuf
843
852
write (newbuf, sbuf)
844
- iolock_end ()
845
- wait_readnb (s, Int (nb))
846
- iolock_begin ()
847
- nb == bytesavailable (newbuf) || throw (EOFError ())
853
+ wait_locked (s, newbuf, Int (nb))
848
854
finally
849
855
s. buffer = sbuf
850
- if ! isempty (s. cond)
851
- start_reading (s) # resume reading iff there are currently other read clients of the stream
852
- end
853
856
end
854
857
end
855
858
iolock_end ()
@@ -860,9 +863,9 @@ function read(this::LibuvStream, ::Type{UInt8})
860
863
iolock_begin ()
861
864
sbuf = this. buffer
862
865
@assert sbuf. seekable == false
863
- while isopen (this) && bytesavailable (sbuf) < 1
866
+ while bytesavailable (sbuf) < 1
864
867
iolock_end ()
865
- wait_readnb (this, 1 )
868
+ eof (this) && throw ( EOFError () )
866
869
iolock_begin ()
867
870
end
868
871
c = read (sbuf, UInt8)
@@ -871,7 +874,7 @@ function read(this::LibuvStream, ::Type{UInt8})
871
874
end
872
875
873
876
function readavailable (this:: LibuvStream )
874
- wait_readnb (this, 1 )
877
+ wait_readnb (this, 1 ) # unlike the other `read` family of functions, this one doesn't guarantee error reporting
875
878
iolock_begin ()
876
879
buf = this. buffer
877
880
@assert buf. seekable == false
@@ -884,25 +887,29 @@ function readuntil(x::LibuvStream, c::UInt8; keep::Bool=false)
884
887
iolock_begin ()
885
888
buf = x. buffer
886
889
@assert buf. seekable == false
887
- if isopen (x) && ! occursin (c, buf) # fast path
888
- preserve_handle (x)
889
- lock (x. cond)
890
- try
891
- while isopen (x) && ! occursin (c, x. buffer)
892
- x. readerror === nothing || throw (x. readerror)
893
- start_reading (x) # ensure we are reading
894
- iolock_end ()
895
- wait (x. cond)
890
+ if ! occursin (c, buf) # fast path checks first
891
+ x. readerror === nothing || throw (x. readerror)
892
+ if isopen (x)
893
+ preserve_handle (x)
894
+ lock (x. cond)
895
+ try
896
+ while ! occursin (c, x. buffer)
897
+ x. readerror === nothing || throw (x. readerror)
898
+ isopen (x) || break
899
+ start_reading (x) # ensure we are reading
900
+ iolock_end ()
901
+ wait (x. cond)
902
+ unlock (x. cond)
903
+ iolock_begin ()
904
+ lock (x. cond)
905
+ end
906
+ finally
907
+ if isempty (x. cond)
908
+ stop_reading (x) # stop reading iff there are currently no other read clients of the stream
909
+ end
896
910
unlock (x. cond)
897
- iolock_begin ()
898
- lock (x. cond)
911
+ unpreserve_handle (x)
899
912
end
900
- finally
901
- if isempty (x. cond)
902
- stop_reading (x) # stop reading iff there are currently no other read clients of the stream
903
- end
904
- unlock (x. cond)
905
- unpreserve_handle (x)
906
913
end
907
914
end
908
915
bytes = readuntil (buf, c, keep= keep)
0 commit comments