Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC: Socket timeouts #1047
Conversation
This comment has been minimized.
This comment has been minimized.
aturon
changed the title
RFC: Socket timesouts
RFC: Socket timeouts
Apr 8, 2015
aturon
force-pushed the
aturon:socket-timeouts
branch
from
dad3282
to
bd88df3
Apr 8, 2015
This comment has been minimized.
This comment has been minimized.
|
Seems good and simple.
|
This comment has been minimized.
This comment has been minimized.
reem
commented
Apr 8, 2015
|
cc me, @seanmonstar |
seanmonstar
reviewed
Apr 8, 2015
| ```rust | ||
| struct WithTimeout<T> { | ||
| timeout: Duration, | ||
| innter: T |
This comment has been minimized.
This comment has been minimized.
alexcrichton
reviewed
Apr 8, 2015
|
|
||
| These methods take an amount of time in the form of a `Duration`, | ||
| which is [undergoing stabilization][duration-reform]. They are | ||
| implemented via straightforward calls to `setsockopt`. |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Apr 8, 2015
Member
In the interest of the "detailed" part of the "Detailed design", could this also mention that the specific options being used are SO_SNDTIMEO and SO_RCVTIMEO?
This comment has been minimized.
This comment has been minimized.
|
It might be worth putting the methods on a |
This comment has been minimized.
This comment has been minimized.
|
It does feel moderately janky to use |
This comment has been minimized.
This comment has been minimized.
netvl
commented
Apr 9, 2015
|
What about connection timeouts? They are usually as important as read timeouts, if not more. |
This comment has been minimized.
This comment has been minimized.
|
@netvl I agree, but that is going to probably require a new RFC as there is currently no way to "configure" socket connection. |
aturon
self-assigned this
Apr 9, 2015
This comment has been minimized.
This comment has been minimized.
|
I'd also like to see if we can stabilize a |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
@sfackler my personal thought was that because |
This comment has been minimized.
This comment has been minimized.
l0kod
commented
Apr 17, 2015
|
|
alexcrichton
referenced this pull request
Apr 19, 2015
Merged
implement set_tcp_keepalive for linux #24594
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
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! |
This comment has been minimized.
This comment has been minimized.
|
A |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
andrew-d
commented
Apr 25, 2015
|
Just throwing this out there, but: is there any reason we can't define
Where |
This comment has been minimized.
This comment has been minimized.
|
@alexcrichton IIRC we discussed only making it an error to send a With that said: is there anyone who strongly prefers to take |
This comment has been minimized.
This comment has been minimized.
|
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. |
sfackler
reviewed
May 4, 2015
| ```rust | ||
| impl TcpStream { | ||
| pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { ... } | ||
| pub fn read_timeout(&self) -> Option<Duration>; |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
aturon
May 4, 2015
Author
Member
Argh, good question! I totally failed to consider errors here. Probably we want io::Result<Option<Duration>>.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
alexcrichton
May 4, 2015
Member
You are correct! :)
I'd be fine with io::Result<Duration> where some error is returned for a nonexistent duration. (e.g. ErrorKind::NotFound)
This comment has been minimized.
This comment has been minimized.
sfackler
May 4, 2015
Member
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.
This comment has been minimized.
This comment has been minimized.
seanmonstar
May 4, 2015
Contributor
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.
This comment has been minimized.
This comment has been minimized.
alexcrichton
May 4, 2015
Member
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.
This comment has been minimized.
This comment has been minimized.
seanmonstar
May 4, 2015
Contributor
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
nagisa
reviewed
May 4, 2015
|
|
||
| ```rust | ||
| impl TcpStream { | ||
| pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { ... } |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
alexcrichton
May 4, 2015
Member
The underlying OS API is just setsockopt which is already threadsafe and we've specifically avoided adding a &mut self barrier on Tcp{Stream,Listener} for example with Read for &T or having accept take &self, even though they're all kinda though of as "mutating" the underlying stream.
In other words, the &mut self isn't necessary, so the primitives don't require it.
nagisa
reviewed
May 4, 2015
| ## 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 |
This comment has been minimized.
This comment has been minimized.
nagisa
May 4, 2015
Contributor
Some(Duration::new(0, 0)) can be mostly back-compatibly be mapped to mark the socket non-blocking at a later time, though.
This comment has been minimized.
This comment has been minimized.
|
I've pushed an update returning |
This comment has been minimized.
This comment has been minimized.
|
Is this good to merge now? My calculations suggest that this missed the 1.1 train :( |
alexcrichton
added
the
T-libs
label
May 18, 2015
This comment has been minimized.
This comment has been minimized.
|
@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? |
This comment has been minimized.
This comment has been minimized.
|
Before this lands, I’d really want for |
This comment has been minimized.
This comment has been minimized.
|
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! |
alexcrichton
merged commit 390b78c
into
rust-lang:master
May 19, 2015
This comment has been minimized.
This comment has been minimized.
|
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 |
seanmonstar
referenced this pull request
Aug 10, 2015
Closed
what's the expected way to detect a timeout error #626
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
Also, it's very possible to take a |
This comment has been minimized.
This comment has been minimized.
softprops
commented
Aug 10, 2015
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 |
This comment has been minimized.
This comment has been minimized.
|
@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 |
aturon commentedApr 8, 2015
Add sockopt-style timeouts to
std::nettypes.Rendered