Live data from Hacker News

Futurelock: A subtle risk in async Rust

rfd.shared.oxide.computer

241–250 of 251 posts

Re: Futurelock: A subtle risk in async Rust

#241
post #183
post #5

Earlier quoted context omitted.

The ideas that have been batted around is called "async drop" [1] And it looks like it's still just an unaddressed well known problem [2]. Honestly, once the Mozilla sackening of rust devs happened it seems like the language has been practically rudderless. The RFC system seems almost dead as a lot of the main contributors are no longer working on rust. This initiative hasn't had motion since 2021. [3] [1] https://ru…

While the Mozilla layoffs were a stressful time with a lot of uncertainty involved, in the end it hasn't appeared to have had a deleterious effect on Rust development. Today the activity in the Rust repo is as high as it's ever been ( https://github.com/rust-lang/rust/graphs/contributors ) and the governance of the project is more organized and healthy than it's ever been ( https://blog.rust-lang.org/2025/10/15/annou…

All major projects need to be done this way. Upfront design without any concrete feedback from a real implementation of that design will always be flawed unless incredibly simple. If anything, the next evolution of this is a process to determine which experiments are worth running to get to a point where you can come up with a sensible design. In organizations I've worked in, the first phase is known as problem framing, which quickly evolved into requirements gathering. From there you come up with several possible loosely defined designs which you pursue in parallel and then at some point, with data in hand you come back and decide on a path forward, possibly needing a few more iterations of experiments based on what you've learned. Reaching agreement early on the problem and requirements really does help the rest of the process move forward, and makes deciding on a final choice much simpler and less political.

Re: Futurelock: A subtle risk in async Rust

#242
post #186

Earlier quoted context omitted.

In fairness, if you're a layman to the rust development process (as I am, so I'm speaking from personal experience here) it's damn near impossible to figure out the status of things. There tracking issues, RFCs, etc which is very confusing as an outsider and gives no obvious place to look to find out the current status of a proposal. I'm sure there is a logic to it and that if I spent the time to learn it would make…

If you want to find out the status of something, the best bet is to go to the Rust Zulip and ask around: https://rust-lang.zulipchat.com/ . Most Rust initiatives are pushed forward by volunteers who are happy to talk about what they're working on, but who only periodically write status reports on tracking issues (usually in response to someone asking them what the status is). Rust isn't a company where documentation…

To be fair, this information is hard to come by in companies as well, unless it's being tracked and reported by program managers. I wonder if it would be sensible for the rust foundation to use some funds to pay folks to help track and organize efforts which the wider community is deeply interested in better. I care about a lot of the things mentioned in this thread, but the cost of paying attention or needing to bother folks on chatrooms to get status prevents me from really staying abreast of the latest.

Re: Futurelock: A subtle risk in async Rust

#243

Earlier quoted context omitted.

Since you are of of the people working directly on this codebase, may I ask you why is select! being used/allowed in the first place? Its footgun-y nature has been known for years (IIRC even the first version of the tokio documentation warned against that) and as such I don't really understand why people are still using it. (For context I was the lead of a Rust team working on a pretty complex async networking progra…

What to use instead?

Your favorite llm will give you several good options. You can use an mpsc channel where you have a task per sender which waits on each branch in the select and then sends a message and then just wait on the receiver end of that channel. Or you could use https://docs.rs/futures/latest/futures/future/fn.select.html (or the select_all version). Both make it more obvious what is going on. The last way would be to implement a future manually. This would probably be my favorite option in many cases as it would be least overhead, but if you've never implemented a future it may be a bit intimidating.

Edit: tokio docs actually provide several suggestions: https://docs.rs/tokio/latest/tokio/macro.select.html#alterna...

Re: Futurelock: A subtle risk in async Rust

#244

Earlier quoted context omitted.

From my understanding this talk describes how he implemented a solution for a similar problem https://www.youtube.com/watch?v=Kvsvd67XUKw I'm saying if you're not writing multi-threaded code everyday, use a library. It can use atomics/locks but you shouldn't use it directly. If the library is designed well it'd be impossible to deadlock.

If you take programming serious, learn it. With a library that encapsulates a low number of patterns (like message passing) you'll be very limited. If you never start learning about lower level multi-threading issues you'll never learn it. And it's not _that_ hard. I'm not writing multi threaded every day (by far), but often enough that I can write useful things (using shared memory, atomics, mutexes, condition varia…

I do write code that uses multi-threading every day and usually I write a few lockless functions every month for the in-house library I use. I was considering writing an article on atomics after all the mistakes and bad code I've seen.

A problem with writing an article is that if I don't show real code, people might think I'm exaggerating; if I do show real code, it'd look like I'm calling someone a bad programmer

Re: Futurelock: A subtle risk in async Rust

#245

Earlier quoted context omitted.

I completely disagree with you and I do write performance code (but not specifically HPC) and my current day job is highly async code with a GUI

Are you saying GUIs in general don't need multithreading or just that you think you haven't needed it so far? Or that you use some high-level async framework that hides just the synchronisation bits (at the cost of async type complexity)?

I use a high-level async framework. It works extremely well so far. I do need to add code occasionally, but that's because it's an in-house lib that isn't a year old yet

Re: Futurelock: A subtle risk in async Rust

#246

Earlier quoted context omitted.

If you take programming serious, learn it. With a library that encapsulates a low number of patterns (like message passing) you'll be very limited. If you never start learning about lower level multi-threading issues you'll never learn it. And it's not _that_ hard. I'm not writing multi threaded every day (by far), but often enough that I can write useful things (using shared memory, atomics, mutexes, condition varia…

I do write code that uses multi-threading every day and usually I write a few lockless functions every month for the in-house library I use. I was considering writing an article on atomics after all the mistakes and bad code I've seen. A problem with writing an article is that if I don't show real code, people might think I'm exaggerating; if I do show real code, it'd look like I'm calling someone a bad programmer

I've certainly seen my share of buggy multi-threaded programming. On the other hand, that's nothing compared to all the buggy and bad code I've seen overall. And I don't think it's going to get better by telling people not to even try.

I'm very doubtful that multi-threading can be abstracted behind a library. Simple message passing can cover a lot of use cases but not everything by far. I've also seen the Javascript model work fine, but it's not real multi-threading (no parallelism). As to async frameworks, they too are restrictive and they come with a lot of complexity.

Re: Futurelock: A subtle risk in async Rust

#247
post #204

Earlier quoted context omitted.

"Not inert" does not at all imply "a single runtime within std+compiler." You've jumped way too far in the opposite direction there. The problem is that the particular interface Rust chose for controlling dispatch is not granular enough. When you are doing your own dispatch, you only get access to separate tasks, but for individual futures you are at the mercy of combinators like `select!` or `FuturesUnordered` that…

If you’ve got eager dispatch I’m eager (pun intended) to learn how you have an executor that’s not baked into the std library and limited to a single runtime per process because at the time of construction you need the language to schedule dispatch of the created future. This is one of the main challenges behind the pluggable executor effort - the set of executors that could be written is so different (work stealing…

You don't need any of that, and you can keep cancellation too.

The core of an eager cooperative multitasking system does not even need the concept of an executor. You can spawn a new task by giving it some stack space and running its body to its first suspension point, right there on the current thread. When it suspends, the leaf API (e.g. `lock`) grabs the current top of the stack and stashes it somewhere, and when it's time to resume it again just runs the next part of the task right there on the current thread.

You can build different kinds of schedulers on top of this first-class ability to resume a particular leaf call in a task. For example, a `lock` integrated with a particular scheduler might queue up the resume somewhere instead of invoking it immediately. Or, a generic `lock` might be wrapped with an adapter that re-suspends and queues that up. None of this requires that the language know anything about the scheduler at all.

This is all typical of how higher level languages implement both stackful and stackless coroutines. The difference is that we want control over the "give it some stack space" part- we want the compiler to compute a maximum size and have us specify where to store it, whether that's on the heap (e.g. tokio::spawn) or nested in some other task's stack (e.g. join, select) or some statically-allocated storage (e.g. on a microcontroller).

(Of course the question then becomes, how do you ensure `lock` can't resume the task after it's been freed, either due to normal resumption or cancellation? Rust answers this with `Waker`, but this conflates the unit of stack ownership with the unit of scheduling, and in the process enables intermediate futures to route a given wakeup incorrectly. These must be decoupled so that `lock` can hold onto both the overall stack and the exact leaf suspension point it will eventually resume.)

Cancellation doesn't change much here. Given a task held from the "caller end" (as opposed to the leaf callee resume handles above), the language needs to provide a way to destruct the stack and let the decoupled `Waker` mechanism respond. This still propagates naturally to nested tasks like join/select arms, though there is now an additional wrinkle that a nested task may be actively running (and may even be the thing that indirectly provoked the cancellation).

Re: Futurelock: A subtle risk in async Rust

#248
post #108

Earlier quoted context omitted.

> So yeah, which of those concerns was async Rust really designed around? All of them I guess? Yes, all of them. Futures needed to work on embedded platforms (so no allocation), needed to be highly optimizable (so no virtual dispatch), and need to act reasonably in the presence of code that crosses FFI boundaries (so no stack shenanigans). Once you come to terms with these constraints--and then add on Rust's other pr…

In my view, the major design sin was not _forcing_ failure into the outcome list. .await(DEADLINE) (where deadline is any non 0 unit, and 0 is 'reference defined' but a real number) should have been the easy interface. Either it yields a value or it doesn't, then the programmer has to expressly handle failure. Deadline would only be the minimum duration after which the language, when evaluating the future / task, wou…

Rust's no_std does not have access to a reliable clock.

Re: Futurelock: A subtle risk in async Rust

#249
post #97

Skimming through, this document feels thorough and transparent. Clearly, a hard lesson learned. The footnotes, in particular, caught my eye https://rfd.shared.oxide.computer/rfd/397#_external_referenc... > Why does this situation suck? It’s clear that many of us haven’t been aware of cancellation safety and it seems likely there are many cancellation issues all over Omicron. It’s awfully stressful to find out while w…

I guess one big question here is whether there's a higher layer abstraction that is available to wrap around patterns to avoid this. It does feel like there's still generally possibilities of deadlocks in Rust concurrency right? I understand the feeling here that it feels like ... uhh... RAII-style _something_ should be preventing this, because it feels like statically we should be able to identify this issue in this…

[deleted]

Re: Futurelock: A subtle risk in async Rust

#250

Earlier quoted context omitted.

> All the Rust evangelists talk about safety when stuff like this exists? JFC. Deadlocks can happen anywhere? You can replicate this pattern in golang.

Golang doesn't have legions of evangelicals claiming it's a safe language and everything should be written in it.

> Golang doesn't have legions of evangelicals claiming it's a safe language and everything should be written in it.

But, to be very clear, Rust has never claimed to prevent deadlocks?

See: https://doc.rust-lang.org/reference/behavior-not-considered-...

Post reply on HN