-
Notifications
You must be signed in to change notification settings - Fork 122
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
Signal safety #16
Comments
The documentation of Read::read says:
And the documentation of Read::read_to_end says:
Finally the documentation of BufRead::read_until says:
But also adds:
In light of the above, I see two solutions to the EINTR problem:
First solutionWith the first solution, any operation either aborts fast, or succeed fully, no matter how many EINTR are encountered. There is no 'in-between' state on the Read/Write objects, the msgpack data is never half-consumed. The current code can be updated trivially to follow this convention. If you call
Second solutionIn the second solution, the decoder is now state-full. It has the advantage of returning quickly on EINTR, at any level of recursion, instead of hiding them. The underlying Read/Write object can be consumed/written to partially. An rmp operation must be re-started until it succeeds to get the Read/Write object to the next sane state.
So looking at the two solutions + the documentation exert above, and the first solution look most reasonable to me. Until you s/EINTR/EAGAIN/ and suddenly, the trade-off is not as clear (hiding EGAIN will basically block any asynchronous loop...). |
For what its worth, note that rustc_serialize/json parser is state-full: http://doc.rust-lang.org/num/rustc_serialize/json/struct.Parser.html |
Wow, great analysis! Thank you! I'd personally prefer the first solution, but only for a set of low-level functions (like reading/writing primitives, maybe bin/str), which doesn't read/write much data. Potential huge-data reading/writing function (like I'll start working with more detailed plan this Sunday. Of course with complete testing suite attached. |
The problem is more wide than I expected before: the current library just works improperly on any I/O errors without external control. How about constraining stateless functions (which knows the required bytearray size >= 2) to accept For example: /// Requires 5 bytes to be read.
fn read_u32<R: BufRead>(rd: &mut R) -> Result<u32> {
let buf = try!(rd.fill_buf());
if buf.len() < 5 {
return Err(Error::InsufficientBytes);
}
let marker = ...;
// Check marker.
let val = ...;
// If everything is ok - consume bytes.
rd.consume(5);
Ok(val)
} On the other hand this wrappers looks like boilerplates... |
This doesn't compose: reading an array requires to read the size + size elements. You could successfully read the size and a couple elements before encountering any error. Then, how do you restart where you left? Note the API of rustc_serialize doesn't work with interruptions. Theirs solution is to parse the json stream into some json value (like your Value type) keeping an explicit stack to handle interruptions in the parser. And then the json Value implements Decodable to decode it into a Rust type. I do like your approach of decoding directly from the stream of data, this does feel more efficient. Since I do not require interruption handling for what I am working on: I always have a buffer containing the whole msgpack in hand; Read will never be interrupted. This is where I like that I can decode directly into the destination type instead of passing trough the overhead of an intermediate Value. |
I restart from the beginning. Until explicitly consumed ReadBuf will return bytes cached. |
It appears that all functions silently handle interruptions by restarting its underlying operations in a proper way. Also I've decided not to save data read on any other I/O error, leaving this action on the user's responsibility. |
This is not what is documented. And not what happens in practice. On UNIX a read on a Socket goes trough the Which in turns call the libc read (http://linux.die.net/man/2/read).
And http://linux.die.net/man/7/socket:
Leaving this action to the user's responsibility is perfectly fine, but then rmp should probably not work on Obviously, |
I mean all rmp's function silently handle interruptions. For non-blocking sockets, usually there are wrappers, which handle Or do you see another solution? |
Some low- and high-level functions are signal unsafe. That means if
Read
orWrite
given returns anErr
withEINTR
there are no way to continue decoding/encoding properly.The text was updated successfully, but these errors were encountered: