-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC: Socket timeouts #1047
RFC: Socket timeouts #1047
Conversation
Seems good and simple.
|
cc me, @seanmonstar |
```rust | ||
struct WithTimeout<T> { | ||
timeout: Duration, | ||
innter: T |
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.
typo innter
👍 Though I agree with @carllerche that we'll want getters too. It might be worth putting the methods on a |
It does feel moderately janky to use |
What about connection timeouts? They are usually as important as read timeouts, if not more. |
@netvl I agree, but that is going to probably require a new RFC as there is currently no way to "configure" socket connection. |
I'd also like to see if we can stabilize a |
@netvl, @carllerche unfortunately a connection timeout is much more complicated than a socket timeout. The options being bound in this RFC have clear implementations on all platforms and have pretty obvious semantics. Connecting with a timeout, however, involves creating a socket, setting it in nonblocking mode, trying to connect, waiting for it to finish, and then finally setting back to blocking mode before returning it. Regardless this sort of functionality would definitely be done through a "socket builder" type instead of the current convenience functions to create sockets if it is to be implemented. |
@sfackler my personal thought was that because |
👍 for a |
I've updated the RFC to be more explicit about the socket options, to add getter methods, and to lay out a possible alternative for using |
I discussed this on IRC briefly, but we may want to specify the truncation behavior here of timeouts a bit specially. The duration RFC recommends truncating in all cases, but this means that on Windows when you specify a 5ns timeout you would actually pass down a 0ms timeout, which is then interpreted as an infinite timeout. I'd personally err on the side of something like:
Morally speaking <1ms timeouts on Windows will be rounded up, but all other timeouts will be rounded down. In a sense this kinda makes sense because 0 in this context represents infinity and rounding down to infinity seems odd, so we just do the next best thing! |
A |
This round-down approach is pretty backwards, given there’s at least one API that is supposed to take at least specified amount of time. Timeout of 0 associates to non-blocking operations, rather than infinity to me. So, when you pass timeout of 0, you either get timeout/EWOULDBLOCK error or something useful happens immediately. For removing timeouts |
As outlined in the alternative the problem with this approach is that
Another alternative @aturon and I were talking about (which I think was omitted from the alternatives by accident) was to return an error on durations such as this. For example specifying 5ns on windows would return an error. This would also possibly open the door to returning an error on |
Just throwing this out there, but: is there any reason we can't define
Where |
@alexcrichton IIRC we discussed only making it an error to send a With that said: is there anyone who strongly prefers to take |
I have updated the RFC to make The question of rounding for Windows is left as an implementation detail, somewhat dependent on how things work out with This RFC should be considered to be in its "final comment period" at this point. |
```rust | ||
impl TcpStream { | ||
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { ... } | ||
pub fn read_timeout(&self) -> Option<Duration>; |
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.
Will this return None
if there's an error calling getsockopt
?
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.
Argh, good question! I totally failed to consider errors here. Probably we want io::Result<Option<Duration>>
.
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.
cc @alexcrichton, I imagine you don't like that idea.
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.
You are correct! :)
I'd be fine with io::Result<Duration>
where some error is returned for a nonexistent duration. (e.g. ErrorKind::NotFound
)
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.
That seems like pretty weird overloading of concepts - I would not consider no timeout to be an error since it's the default case!
Is Result<Option<Duration>>
really that bad? I'd assume that 99% of users are going to let timeout = try!(foo.write_timeout());
so won't even run into the double indirection.
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.
Yea, having try!(s.read_timeout())
return an error for no timeout would be confusing. There wasn't an error reading it! Result<Option<Duration>>
is the correct signature in this case.
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.
I personally see Result<Option<Duration>>
as quite bad (too much nesting). I re-consulted the documentation for getsockopt
and it states
Note that not all implementations allow this option to be retrieved.
This means that a successful set_write_timeout
may not be followed by a successful write_timeout
so you're probably not just try!
-ing away the error in the robust case anyway.
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.
The way it would be represented in other languages matches Result<Option<Duration>>
. For instance, Python would be:
try:
timeout = so.gettimeout()
if timeout is not None:
# whatever
except OSError, e:
# handle error
## Taking `Duration` directly | ||
|
||
Using an `Option<Duration>` introduces a certain amount of complexity | ||
-- it raises the issue of `Some(Duration::new(0, 0))`, and it's |
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.
Some(Duration::new(0, 0))
can be mostly back-compatibly be mapped to mark the socket non-blocking at a later time, though.
I've pushed an update returning |
Is this good to merge now? My calculations suggest that this missed the 1.1 train :( |
@seanmonstar It's been in it's final comment period for two weeks, so it seems good to me. (We're still getting the subteams fully up and running.) @alexcrichton thoughts? |
Before this lands, I’d really want for |
I agree with @aturon that I believe this RFC has baked long enough that this is ready for acceptance. There is broad support for the API proposed here as well as the semantics of what'll happen on Windows and Unix, and the case @nagisa brought up for nonblocking sockets is a backwards-compatible extension that can be made (and the implementations will initially land as As a result, I'm going to merge this and open an issue for tracking the implementation, thanks for the comments everyone! |
Not sure where ongoing discussion of this should go, so trying here. Currently, after setting a timeout, if a read does trigger a timeout, the error returned is /cc @softprops |
I considered that during the implementation, but we decided against that under the rationale that it was too magical - io::Error maps directly down to errno and seems a bit weird to change that in random cases. Checking if the socket is in non-blocking mode/has a timeout would each be a system call, which seems like it would violate the "direct bindings" philosophy of the current IO module. For example, a call to It might make sense to add a method to |
Also, it's very possible to take a |
There was an interesting conditional in a test in the pr that seemed like it hinted at the need for that https://github.com/rust-lang/rust/pull/25818/files#diff-e1b6aa0742f9015d1840084d1925ece9R941 |
@carllerche yea, thus the suggestion of checking if the socket is non-blocking. @sfackler it wouldn't be the first time that an API was normalized because of OS differences: On Windows, a read return value of |
Add sockopt-style timeouts to
std::net
types.Rendered