October 31, 2025

When ‘await’ becomes ‘forever’

Futurelock: A subtle risk in async Rust

Rust hits a weird freeze and the comments go feral — is async the bad guy?

TLDR: A tricky Rust async pattern called “futurelock” can freeze a program when a canceled task still holds a lock. Commenters split between calling for stricter safety rules and switching to actor-style design, while war stories and JS curiosity fuel the drama—because hidden deadlocks can take down real systems.

Rust devs just met “futurelock,” the bug with the horror-movie name: a subtle freeze where a task waits on itself because a piece of work is still holding the “key” it needs. The example? One background job holds a lock, the main code races a task and a short timer, and somehow the whole thing jams. The shocker: cancelling a task doesn’t always mean it gets dropped right away—so it keeps clutching the lock like a dragon hoarding gold. Cue gasps, facepalms, and a thousand “WTF Rust?” memes.

The comments are the real show. One camp wants Rust to step in with stricter rules: drop stuff before you await, or else. Another crew is teaching Cancellation 101: a future can stop getting polled but still not be dropped, which is where the chaos begins. The spicy take? The eternal “actors vs async” brawl is back, with folks praising Erlang’s actor model as the cleaner, safer way. Meanwhile, JavaScript folks roll in like, “Could this happen to us?” and start copy-pasting to find out. Expect more RFCs and more drama.

Jokes flew: “dread-lock,” “Schrödinger’s future: cancelled but not dropped,” and “tokio drift.” It’s funny—until your production service freezes at 3 a.m. Then it’s a very unfun puzzle that finally clicks together… after hours of pain.

Key Points

  • “Futurelock” is defined as a deadlock where a resource owned by one future blocks another future after the task stops polling the first without dropping it.
  • A Tokio example demonstrates a reliable deadlock using a shared Mutex lock, a background task, and tokio::select!.
  • future1 attempts to acquire the lock and returns Pending; after sleep completes, select! switches to another branch that also needs the lock.
  • The first future is no longer polled but not dropped, potentially holding a guard or queue position, preventing the second future from acquiring the lock.
  • The article clarifies that cancellation can mean stopping polling without dropping, and delayed dropping can produce side effects that cause deadlock.

Hottest takes

“force users to not hold a variable across await points” — Sytten
“what made you decide to go for the async design pattern instead of the actor pattern?” — jacquesm
“the future stops getting polled… the drop is delayed” — oconnor663
Made with <3 by @siedrix and @shesho from CDMX. Powered by Forge&Hive.