-
-
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
Wait for messages in the error queue #5716
Comments
Do you have any resources that explain what the error queue is? |
Fair question, this stuff is kind of scattered around the internet. Also I have only really worked with this one use case, but here goes: On unix systems, we have ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); One of the flag bits is
the docs page provides some further information on the actual content of the data: https://man7.org/linux/man-pages/man2/recvmsg.2.html. The received values are called "control messages", which have a bunch of different uses. Sadly, there is not some nice exhaustive list of when something actually ends up in the error queue. Generally speaking it allows you to get some extra info on what actually went wrong with a socket operation, e.g.: // Iterate through control messages
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
// Check if it's a IPPROTO_IP control message with type IP_RECVERR
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVERR) {
ee = (struct sock_extended_err*)CMSG_DATA(cmsg);
// Check if it's a destination unreachable error
if (ee->ee_origin == SO_EE_ORIGIN_ICMP && ee->ee_type == ICMP_DEST_UNREACH) {
struct sockaddr_in* addr = (struct sockaddr_in*)ee->ee_info;
char ipstr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(addr->sin_addr), ipstr, INET_ADDRSTRLEN);
printf("Destination Unreachable: Host %s is unreachable.\n", ipstr);
// Handle the unreachable condition appropriately
// ...
}
}
} Besides errors, this mechanism is also (ab)used to send ancillary data. One (wild) use case is to send file descriptors between processes. this blog post has some details for the curious reader. (https://man7.org/linux/man-pages/man7/unix.7.html, search for "ancillary") Timestamps also fall into this ancillary data category. The socket is configured to generate timestamps, and the timetamp control messages appear on the error queue after the IO operation is done. Is that helpful? there isn't really a good central place that explains what the error queue is, as far as I can find. so I tried to give some extra context here. |
Not sure if this applies here, but Rust currently has experimental support for socket ancillary data. See experimental types here: https://doc.rust-lang.org/std/os/unix/net/index.html and also rust-lang/rust#76915 |
So, this is the same as socket ancillary data? |
socket ancillary data is a part of it, although the current https://doc.rust-lang.org/std/os/unix/net/enum.AncillaryData.html seems to implement only a very limited set of control messages that can be received. In practice the pattern match at https://doc.rust-lang.org/src/std/os/unix/net/ancillary.rs.html#394 has way more branches. But I don't think all that really belongs in In general the error queue is an additional mechanism for receiving data on a socket. It was initially designed for receiving extra error info without meddling with further "normal" data bytes that the socket was also receiving, but has also been (ab)used for other kinds of information. I hope this is useful background information, but I'd like to clarify again that I don't think tokio should concern itself with receiving messages on the error queue. it's very low-level and niche, and hard to capture in a nice and efficient api in general. What I'm asking for specifically is the ability to await something entering the error queue, which is equivalent to waiting for a mio event where |
So, the solution I propose does not actually work. When an interest is just the hypothetical
and so that means we're kind of stuck here. As I mentioned in the OP an arbitrary That approach is recommended on this mio issue.
I still think that is inelegant, and at least But the I also made a mio version of the earlier example: gist. |
Add `Ready::ERROR` enabling callers to specify interest in error readiness. Some platforms use error readiness notifications to notify of other events. For example, Linux uses error to notify receipt of messages on a UDP socket's error queue. Using error readiness is platform specific. Closes #5716
Is your feature request related to a problem? Please describe.
I work on ntpd-rs, an NTP implementation in rust. A big part of how it works is to configure a UDP socket to record timestamps when a message is sent or received, and then retrieving these timestamps. The timestamp is not available immediately (we've observed this for external hardware clocks, software timestamps do seem to be effectively instant).
These timestaps end up in the error queue (libc::MSG_ERRQUEUE) of the socket. Tokio does not have a way to await or read these messages currently. The reading of these messages is probably too low-level for tokio, but being able to await the messages appearing would be extremely useful.
Describe the solution you'd like
With #5566 heading in a promising direction, I think from a user perspective, the best API would be
Adding
Ready::ERROR
is straightforward. But adding an error interest is really not. Currently, a tokio interest is just a wrapper around themio::Interest
:But
mio::Interest::ERROR
does not exist, with a good reason: the error "interest" is always registered by the OS (see tokio-rs/mio#1672 (comment)). Therefore (not) usingmio::Interest::ERROR
would have no effect either way, and would be confusing.But for public tokio APIs, the interest is specifically what you listen for, and hence what you'll be notified for. That would mean that tokio would have to implement
Interest
itself (sort of inlining the mio implementation). I don't see any technical issues with that approach, and I don't think there are performance downsides really: all of the logic to convert tomio::Interest
could useconst fn
.But it's kind of inelegant because it duplicates a bunch of logic from mio, and increases maintenance burden and the change of inconsistencies. I'd say it's worth it, but there is a cost.
Describe alternatives you've considered
Another option would be to always include the error readiness, like mio:
this would sort of work, but it's extremely inelegant. The example from earlier would have to pick a different, arbitrary
Interest
, and then just ignore this interest and just see if the readiness includes the error flag.I also think this will lead to problems with e.g.
readable
firing but a read stil blocking. Code should be robust against that, but it's still inefficient to read when we know it would block.Additional context
The error queue is used for many things besides timestamps. Here is an example that puts a explicitly puts a message on the error queue. It may come in handy for testing. The examples that I know of are all from unix systems, but I don't see any harm in exposing error readiness across platforms (mio already supports it anyway).
I'm happy to work on this, but would like some confirmation on this path not being totally unreasonable before starting.
The text was updated successfully, but these errors were encountered: