Async Mutexes
Key topics
The article discusses the challenges and design considerations of implementing async mutexes in single-threaded, cooperative multitasking systems, sparking a discussion on the necessity and proper use of mutexes in such contexts.
Snapshot generated from the HN discussion
Discussion Activity
Active discussionFirst comment
11d
Peak period
13
Day 11
Avg / period
11
Based on 22 loaded comments
Key moments
- 01Story posted
Nov 4, 2025 at 7:02 AM EST
2 months ago
Step 01 - 02First comment
Nov 14, 2025 at 10:23 PM EST
11d after posting
Step 02 - 03Peak activity
13 comments in Day 11
Hottest window of the conversation
Step 03 - 04Latest activity
Nov 15, 2025 at 11:26 AM EST
about 2 months ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
Throwing an async mutex to fix the lack of atomicity before yielding is basically telling me that "i don't know when I'm calling await in this code so might as well give up". In my experience this is strongly correlated with the original designer not knowing what they are doing, especially in languages like JavaScript. Even if they did understand the problem, this can introduce difficult-to-debug bugs and deadlocks that would otherwise not appear. You also introduce an event queue scheduling delay which can be substantial depending on how often you're locking and unlocking.
IMO this stuff is best avoided and you should just write your cooperative multi-tasking code properly, but this is does require a bit more advanced knowledge (not that advanced, but maybe for the JS community). I wish TypeScript would help people out here but it doesn't. Calling an async function (or even normal functions) does not invalidate type narrowing done on escaped variables probably for usability reasons, but is actually the wrong thing to do.
[1]: https://www.npmjs.com/package/async-mutex
It looks eerily familiar to Spring DI framework, yikes.
Developers should properly learn the difference between push and pull reactivity, and leverage both appropriately.
Many, though not all, problems where an async mutex might be applied can instead be simplified with use of an async queue and the accompanying serialization of reactions (pull reactivity).
I got especially confused at the actor part, aren't actors share nothing by design?
It does to say there's only 1 actor, so I guess it has nothing to do with actors? They're talking about GenServer-like behavior within a single actor.
As I write this, I'm figuring out maybe they mean it's like a single actor GenServer sending messages to itself, where each message will update some global state. Even that though, actors don't yield inside callbacks, if you send a message it gets queued to run but won't suspend the currently running callback, so the current method will run to completion and then the next will run.
Erlang actors do single-threaded actors with serialized message processing. If I understand the article, that avoids the issue it brings up completely as you cannot have captured old state that is stale when resuming.
Me as well. I thought actors are a bit like mailboxes that process and send their messages.
The tl;dr is that it uses garbage collection to let readers see the older version of the tree they were walking while the latest copy still gets updated.
I read a paper recently about concurrent interval skip lists the other day as well which was interesting.
It may seem like single-threaded execution doesn't need mutexes, but it is a fact that async/await is just another implementation of userspace threads. And like all threads, it may lead to data races if you have yield points (await) inside a critical section.
People may say what they want about bad design and reactive programming, but thread model is inherently easier to reason about.
https://en.wikipedia.org/wiki/Non-blocking_algorithm#Obstruc...
When I compact, I take read locks on victims A and B while I use them to produce C. So callers can still query A and B during compaction. Once C is produced, I atomically add C and remove A and B, so no caller should see double or missing records.
4 more comments available on Hacker News