How Do You Build a Mutex That Works with async/await ?


A couple of days ago I ran into a really interesting use case: I wanted to protect a method from being called more than once at a time. You might think "actor, done" β€” but nope, an actor won't save you here.

Because of actor reentrancy, the moment your method hits an await and suspends, another task can sneak in and start running the same method.

Mutual exclusion across suspension points? Not guaranteed.

The classic tool for "only one thing touches this at a time" is a Mutex β€” in general CS terms, a primitive that ensures only one thread or task can access a specific resource at any given moment. And with the new Synchronization framework, we finally have Mutex available natively in Swift.

In my case the "resource" I needed to protect wasn't a piece of state β€” it was the act of calling a method that mutates the backend database. I wanted to make sure no two tasks could ever be inside it simultaneously.

Here's the catch: my method is async, and Mutex doesn't play with async/await at all. You can't await inside its critical section. πŸ€”

That's when I reached out to Matt Massicotte, and he pointed me to TaskGate β€” his package built exactly for this.

It exposes two types, AsyncGate and AsyncRecursiveGate, that let you define asynchronous critical sections: only one task enters at a time, but unlike a traditional lock, you can safely await while the gate is held.

I found it genuinely elegant, so I kept digging through the source to understand exactly how it works β€” and today I'm sharing that with you.

Before we dive in, a special thanks to Matt Massicotte. Without his guidance I'd still be lost in Modern Concurrency. Thank you for everything you share with the community, and for your contributions to Swift itself. πŸ™
​
​https://www.swiftdifferently.com/blog/swift/concurrency/how-do-you-build-a-mutex-that-works-with-async-await​

600 1st Ave, Ste 330 PMB 92768, Seattle, WA 98104-2246
​Unsubscribe Β· Preferences​