Never snooze a future(jacko.io)
16 points byvinhnx4 days ago |3 comments
yoshuaw12 minutes ago
For the past seven years I've been urging people to stop using `select!` in async Rust code and use dedicated structured control flow primitives instead. Correct use of `select!` cannot be guaranteed by the compiler [1], and so it's unsurprising it leads to bugs.

For the first example the better option would be to use the `race` operation [2]. For the second it would be better to use `ConcurrentStream` [3]. Many (but not all) of the issues people have with cancellation in async Rust can be improved with the advice: "don't use select!".

[1]: https://blog.yoshuawuyts.com/futures-concurrency-3/#issues-w...

[2]: https://docs.rs/futures-concurrency/latest/futures_concurren...

[3]: https://docs.rs/futures-concurrency/latest/futures_concurren...

cousin_it1 hour ago
Looks like the code of foo() says "take a lock then sleep for 10 millis", but actually it can take the lock and then sleep forever, depending on how it's polled. Well! This seems like a bug with the async abstraction in Rust then. Or if you don't like "bug", then "disagreement with intuition that will cause bugs forever". Goroutines in Go don't have this problem: if a goroutine says it'll take a lock and sleep for 10 millis, then that's what it'll do. The reason is because Go concurrency is preemptive, while async in Rust is cooperative.

So maybe the lesson I'd take is that if you're programming with locks (or other synchronization primitives), your concurrency model has to be preemptive. Cooperative concurrency + locks = invitation to very subtle bugs.

rcxdude49 minutes ago
I don't think it's preemptive vs cooperative that matters. What Rust's abstraction allows is for a function to act like a mini-executor itself, polling multiple other futures itself instead of delegating it to the runtime. That allows them to contain subtle issues like stopping polling a future without cancelling it, which is, yeah, dangerous if one of those futures can block other futures from running (another way you could come at this is to say that maybe holding locks across async points should be avoided).
cousin_it2 minutes ago
> holding locks across async points should be avoided

Hold on, what's the point of taking a lock if you're gonna release it without calling any awaits, if nothing can interfere anyway? Or do you mean cases where you have both cooperative and preemptive concurrency in the same program.

ixxie2 hours ago
I thought this was a "carpe diem" motivational post xD