-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
util: makes Framed
and FramedStream
resumable after eof
#3272
util: makes Framed
and FramedStream
resumable after eof
#3272
Conversation
The current behaviour of FramedRead::poll_next did not align with the behaviour of AsyncReadExt, where read operations might only temporarily return 0 bytes (signalling EOF). And provided no way to recover from such an event, behaving similar to a FusedStream. Furthermore decode_eof could be called with repeated read attempts on a stream that already returned None, potentially providing a source of unexpected/erroneous closing frames. The new behaviour allows for Frame consumption to continue even after an EOF event, iff the underlying AsyncRead has more data, as with files and FIFOS. After decode_eof has returned None it will not be called again until new data was read and a new EOF event has been encountered. Fixes: tokio-rs#3252
The behaviour described in tokio-rs#3252 is now better documented. Including an example on how to resume a framed stream.
Looks like I've fixed the merge conflict too hastily. The correct trait is imported in the test now.
Sorry for the delay in reviewing. We were busy with getting Tokio 1.0 out. The state machine is somewhat difficult to understand. The name |
Hey @Darksonn I've updated the state machine notes to use the states instead of the flags, sorry that was my error, with the problem still cached in my brain I didn't make the proper abstractions. I've also included an ASCII drawing of the state machine and the transitions it takes when the flags change. To answer your question, yes that's possible. It's part of the Merry winter solstice, thanks for checking this out in-between the years 🎄 🎁 ❤️ ✨! |
Adds ASCII art state machine drawing to better document the workings of `framed_impl`. Also improved the documentation text to make the `is_readable` flag more clear.
This annotates the code in `poll_next` with information on which state a code-path might be in, which transitions a flag change might prepare, and which transitions a loop continuation or return might actually commit to.
This documents the implicit stopped -> stopped transition that occurs when a read is pending.
All credit goes to @Darksonn! Co-authored-by: Alice Ryhl <alice@ryhl.io>
Removed unnecessary `buffer` capacity check. Cleaned up inline documentation. Fixed stopped state name to paused.
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.
Can you add some tests? Then I think this is ready.
Sorry for the delay in reviews.
credit goes to @Darksonn Co-authored-by: Alice Ryhl <alice@ryhl.io>
Sorry, I completely forgot about this. I will merge it when CI passes. |
No need to apologize. Thanks a lot for writing those tests! Also thanks for your effort put into merging this. |
Motivation
As described in #3252 there are cases where underlying data sources might only temporarily return 0 bytes/ eof.
Framed
andFramedRead
can now resume reading such sources even after an initial eof.This PR also prevents unexpected calls to
decode_eof
when frames are read after the frame stream has returnedNone
.For my use case of reading an append only logfile, I am now able to read frames until an an eof,
and then wait for an IPC notification on more available data.
PR also improves the documentation a bit by adding a general example for
Framed
.Solution
The previous implementation had the assumption that 0 bytes returned from the underlying implementation implies that it will always do so. I've reused the existing state flags, and changed the control flow so that the the following 4 states are now possible.
pausing
, if bytes were available it goes toframing
decoder.decode
, staying in this state until unsuccessful, in which case it will go toreading
decoder.decode_eof
until thedecoder
declares no more closing frames throughNone
, after which it goes topaused
reading
, in that it attempts to load more data into the buffer when called, except that reading no bytes directly returnsNone
, thus staying in thepaused
state without emitting any closing frames, goes toframing
on successful readIt is up to the
Decoder
to handle restarts (aka. transitions frompaused
toframing
) by implementing an appropriate policy.Full disclosure: I've only been programming rust in anger for 10 days.
This solves the issues I had, but I might be ignorant of
Steam
,Sink
and other rust semantics and best practices.