|
|
Subscribe / Log in / New account

Error in code sample?

Error in code sample?

Posted Sep 27, 2025 10:14 UTC (Sat) by Nikratio (subscriber, #71966)
Parent article: Canceling asynchronous Rust

From the article:

> This code won't drop msg if a timeout occurs:
>
> loop {
> let msg = next_message();
> loop {
> match timeout(Duration::from_secs(5), tx.reserve()).await {

I think that's wrong, this code will drop `msg` on timeout just like the previous example. Not sure if that's a mistake in the article or the slides?


to post comments

Error in code sample?

Posted Sep 27, 2025 16:34 UTC (Sat) by excors (subscriber, #95769) [Link] (3 responses)

I don't see the problem there: On timeout it will run the `println!()` arm, then it will go around the inner `loop` and run the `match` to try reserving a slot again, without touching `msg`. The `Ok(Ok(_))` arm passes ownership to `send`, so only the `Ok(Err(_)) => return` arm (for non-timeout errors) will drop `msg`.

Error in code sample?

Posted Sep 28, 2025 9:53 UTC (Sun) by Nikratio (subscriber, #71966) [Link] (2 responses)

Ah, you're right, got to work on my reading comprehension :-).

I got "timeout" mixed up with "cancellation", since the previous paragraph (and the article overall) is about cancel-correctness. The code handles timeouts correctly, but still drops the message on cancellation.

I think a version that handles both wouldn't be much harder, so I'm surprised that the sample didn't look something like this:

loop {
match timeout(Duration::from_secs(5), tx.reserve()).await {
Ok(Ok(permit)) => {
msg = next_message();
permit.send();
}
Ok(Err(_)) => return,
Err(_) => println!("No space for 5 seconds"),
}
}

Error in code sample?

Posted Sep 28, 2025 11:32 UTC (Sun) by excors (subscriber, #95769) [Link]

I think the Ok(Err) case doesn't count as cancellation, it's just normal synchronous error handling. Cancellation is specifically when a Future is dropped before it has completed (i.e. before its `poll` function has returned Poll::Ready(_)). In this case tx.reserve() constructed the Future, and its `poll` has returned Poll::Ready(Err(_)), so the Future has completed and there is no cancellation.

In contrast, `timeout` constructs a new Future that wraps the original Future, and (on timeout) the outer Future completes but the inner Future is dropped (cancelled). And any resources owned by the inner Future (e.g. `msg`, in the original `timeout(tx.send(msg))` example) are dropped too, which is the issue being solved by the later example.

In practice the code should have some better error handling for Ok(Err) that isn't simply `return;`, but maybe e.g. it's going to reset the whole channel and the old message is redundant so it's okay to drop it; that will depend on context and is outside the scope of this example.

I think in general you shouldn't move the next_message() call in between reserve() and send(), because next_message() might be expensive and perhaps async itself; you might hold the reservation for a very long time, wasting the channel's capacity. You'd need a different way to handle `msg` in the error case, if not simply dropping it.

Error in code sample?

Posted Sep 30, 2025 0:31 UTC (Tue) by riking (subscriber, #95706) [Link]

The crux of the example is that the `timeout` function is invoking cancellation in a way that the code author didn't notice.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds