Skip to content
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

docs: stream Reads are not safe for concurrent use #140

Merged
merged 1 commit into from
Dec 4, 2024

Conversation

tgross
Copy link
Member

@tgross tgross commented Dec 3, 2024

In #136 we documented warnings around the semantics of calling Stream.Read concurrently. But we assumed without investigating that the concurrent operations were actually safe. But in fact the locks made by multiple Read calls can interleave, causing a deadlock. Update the documentation again to make this clear.

Fixes: #139

@tgross tgross requested a review from a team as a code owner December 3, 2024 15:26
@tgross tgross requested a review from schmichael December 3, 2024 15:26
@lukaspj
Copy link

lukaspj commented Dec 3, 2024

Do you think that stream closure can still be safely detected by reading an empty array like this example?
#115 (review)

Or do we need another pattern?

@schmichael
Copy link
Member

Do you think that stream closure can still be safely detected by reading an empty array like this example? #115 (review)

Or do we need another pattern?

That pattern is fine as long as you do not expect data or concurrent Reads on that Stream. At least until we fix the deadlock there should only be 1 Reader and 1 Writer per Stream.

That being said, even if we fix the deadlock I can not think of a case where multiple Readers on a single Stream is coherent or at least optimal. Multiple concurrent Readers would receive different chunks of Writes to the Stream, and lose the order they came in which would prevent reassembly. I cannot think of a way that multiple concurrent Readers (with buffers) could produce coherent results.

Your use case of splitting "main" Reads and EOF detection into 2 goroutines doesn't run into coherency issues, but it still isn't optimal as on data the empty-buffer Reader will spin in a tight loop until the main Reader actually Reads the buffer. In practice you may only see 0-2 empty-buffer Reads before the main Reader picks up the data. However, I would suspect in loaded conditions where the main Reader goroutine may spend a significant amount of time performing operations on received data before calling Read again: the empty-buffer Reader may spin quite a lot and waste considerable CPU time.

So my recommended pattern, regardless of deadlock/race-condition bugs, is to always have at most 1 Reader and 1 Writer goroutine for yamux Streams (any goroutine can call Close). Your main Reader should already be detecting and handling EOFs, so hopefully removing the empty-buffer goroutine doesn't complicate your code. You can always keep your EOF-detection goroutine, give it a buffer, and on data copy the buffer over a channel or pipe to the main Reader instead of having the main Reader use the Stream directly.

In #136 we documented warnings around the semantics of calling `Stream.Read`
concurrently. But we assumed without investigating that the concurrent
operations were actually safe. But in fact the locks made by multiple Read calls
can interleave, causing a deadlock. Update the documentation again to make this
clear.

Fixes: #139
@tgross tgross force-pushed the docs-stream-concurrency branch from ac4088f to febf95a Compare December 3, 2024 18:17
@tgross tgross changed the title docs: streams are not safe for concurrent use docs: stream Reads are not safe for concurrent use Dec 3, 2024
@lukaspj
Copy link

lukaspj commented Dec 3, 2024

Ah yeah not an issue for us at all, just need to know what we can and can’t do then we’re good 😊
Thanks a lot! Really appreciate all the time you put into this!

Copy link
Contributor

@mukeshjc mukeshjc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tgross tgross merged commit 060257c into master Dec 4, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Race Condition on Concurrent Reads
4 participants