-
-
Notifications
You must be signed in to change notification settings - Fork 3
Flush pending writes before reading from buffer #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -97,12 +97,19 @@ def read(size = nil, buffer = nil) | |
| end | ||
|
|
||
| if size | ||
| until @finished or @read_buffer.bytesize >= size | ||
| # Compute the amount of data we need to read from the underlying stream: | ||
| read_size = size - @read_buffer.bytesize | ||
|
|
||
| # Don't read less than @minimum_read_size to avoid lots of small reads: | ||
| fill_read_buffer(read_size > @minimum_read_size ? read_size : @minimum_read_size) | ||
| if @finished or @read_buffer.bytesize >= size | ||
| # We have enough data in the read buffer, but we should still flush pending writes: | ||
| self.flush | ||
| else | ||
| while true | ||
| # Compute the amount of data we need to read from the underlying stream: | ||
| read_size = size - @read_buffer.bytesize | ||
|
|
||
| # Don't read less than @minimum_read_size to avoid lots of small reads: | ||
| fill_read_buffer(read_size > @minimum_read_size ? read_size : @minimum_read_size) | ||
|
|
||
| break if @finished or @read_buffer.bytesize >= size | ||
| end | ||
|
Comment on lines
+104
to
+112
|
||
| end | ||
| else | ||
| until @finished | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1011,6 +1011,8 @@ def after(error = nil) | |||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| describe "Socket.pair" do | ||||||||||||||
| include Sus::Fixtures::Async::ReactorContext | ||||||||||||||
|
|
||||||||||||||
| let(:sockets) {Socket.pair(:UNIX, :STREAM)} | ||||||||||||||
| let(:client) {IO::Stream::Buffered.wrap(sockets[0])} | ||||||||||||||
| let(:server) {IO::Stream::Buffered.wrap(sockets[1])} | ||||||||||||||
|
|
@@ -1022,6 +1024,29 @@ def after(error = nil) | |||||||||||||
|
|
||||||||||||||
| it_behaves_like AUnidirectionalStream | ||||||||||||||
| it_behaves_like ABidirectionalStream | ||||||||||||||
|
|
||||||||||||||
| it "flushes pending writes even when read buffer already has data" do | ||||||||||||||
| # When read-ahead pulls more data than requested, a subsequent read can | ||||||||||||||
| # complete from the buffer without calling fill_read_buffer — skipping | ||||||||||||||
| # flush. This test verifies that pending writes are still flushed. | ||||||||||||||
| task_a = reactor.async do | ||||||||||||||
| client.write("A1") | ||||||||||||||
| data = client.read_exactly(2) | ||||||||||||||
|
||||||||||||||
| data = client.read_exactly(2) | |
| client.read_exactly(2) |
Copilot
AI
Mar 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As written, a regression will manifest as a deadlock and this example will hang indefinitely (waiting on task_a/task_b) rather than failing fast. Please wrap the waits in a bounded timeout (using the Async/Sus reactor context utilities) so CI gets a deterministic failure instead of a stuck test run.
| task_a.wait | |
| task_b.wait | |
| reactor.with_timeout(1) do | |
| task_a.wait | |
| task_b.wait | |
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
read(size)now callsflushwhen@finishedis true. That means a buffered read after EOF can raise (e.g. EPIPE/IOError from flushing pending writes) and prevent returning data that’s already in@read_buffer, which is a behavior change from before. If the intent is only to fix the “buffer satisfied size so fill_read_buffer is skipped” case, consider only flushing when@read_buffer.bytesize >= size(or otherwise ensure EOF-buffered reads can still succeed without being interrupted by write-side errors).