Tuesday, April 30, 2024

Swift Proposal: Synchronous Mutual Exclusion Lock

SE-0433:

Not all code may be able (or want) to adopt actors. Reasons for this can be very varied, for example code may have to execute synchronously without any potential for other tasks interleaving with it. Or the async effect introduced on methods may prevent legacy code which cannot use Swift Concurrency from interacting with the protected state.

[…]

We propose a new type in the Standard Library Synchronization module: Mutex. This type will be a wrapper over a platform-specific mutex primitive, along with a user-defined mutable state to protect.

[…]

Mutex will be decorated with the @_staticExclusiveOnly attribute, meaning you will not be able to declare a variable of type Mutex as var. These are the same restrictions imposed on the recently accepted Atomic and AtomicLazyReference types. […] We do not want to introduce dynamic exclusivity checking when accessing a value of Mutex as a class stored property for instance.

[…]

Mutex is unconditionally Sendable regardless of the value it's protecting. We can ensure the safetyness of this value due to the transferring marked parameters of both the initializer and the closure inout argument.

On Apple platforms, this is based on os_unfair_lock, which is a bit tricky to directly use from Swift because you have to manage the memory for the lock yourself to prevent Swift from moving it. macOS 13 brought OSAllocatedUnfairLock, which is Apple’s implementation that handles this for you. Mutex is better still because it doesn’t need to allocate a separate block of memory for the lock.

Gwendal Roué:

My experience with Swift Concurrency might be somewhat particular.

[…]

And I’m not sure I’ll replace DispatchQueues with any Swift concurrency constructs any time soon, because a demanding user of SQLite won’t accept to be limited to strictly serialized accesses. SQLite supports concurrent database accesses, including parallel reads and writes. Indeed databases support concurrency in a way that is almost never found in any memory-based synchronization primitive: actors, mutexes, read-write locks are all too limited.

Final nail in the coffin: a demanding SQLite user expects to be able to perform synchronous database accesses at will (i.e. when needed) – just like the regular C API – without any risk of concurrency bugs. There’s currently no Swift concurrency construct that allows both sync and async accesses.

Kyle Howells:

I can’t help feeling Swift’s async, Actor, isolation system is a mistake.

Rather than making the safe thing the default, even if a bit slow, and making a slightly awkward API for a “fast mode” it makes most code think about async isolation most of the time.

Previously:

Comments RSS · Twitter · Mastodon

Leave a Comment