BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Swift Aims to Become a Data Race-Free Concurrent Language

Swift Aims to Become a Data Race-Free Concurrent Language

This item in japanese

The Swift team has published its roadmap to improve concurrency support in Swift. In a first phase, Swift will gain the async syntax and actors, while in a second phase focus will be on eliminating data races and deadlocks.

The Swift concurrent programming model will not change at its heart and keep its basic philosophy of preferring lock-free operations dispatched on independent queues and returning their results through callbacks. This approach has its own drawbacks in terms of coding overhead, lack of efficiency, and being error prone, and programmers have to ensure they scrupulously follow the right patterns to guarantee correctness and performance.

So the patterns shown here are good, but expressing them in Swift loses important structure and creates problems. The solution is to bring those patterns into the language.

Based on coroutines, the basic async/await syntax in Swift will look like in the following snippet:

internal func asyncMethod() async {
  result = await anotherClass.anotherAsyncMethod()
}

One of the benefits of this syntax is it makes away with the requirement of being explicit when capturing self in a closure. A peculiar trait of Swift async/await syntax is the await keyword must be specified only once at the start of an expression containing async code, and not be repeated before each async function.

Alongside async and await, Swift will also introduce an actor type. This will be a shorthand to define a class owning a private queue used to synchronize all accesses to its internal state via serialization.

actor class AnActorClass {

  // some state

  func modifyState() async { ... }
}

When using the code above, the compiler will make sure all async methods will run on the actor's private queue and flag any attempt to access its state directly. This will open up a number of possible optimizations which could benefit performance.

Swift will also support a specific syntax to tie async methods in a class to a specific global actor. This would be the case with the UIActor used to synchronize UI operations, which must happen on the main queue. For example, you could have:

@UIActor
class AClassDoingUIStuff {
    func doSomeUITask() async { ... }
}

In a second phase, the Swift team will add full actor isolation to prevent data races and deadlocks.

The basic idea of actor isolation is similar to the idea of exclusive access to memory, and builds upon it. Swift’s concurrency design aims to provide an easy-to-use and composable approach to safe concurrency by starting from the natural isolation of actors and then using ownership as a complementary tool.

Actors will provide basic isolation by default. This means they will protect their internal state from data races, be it properties, let constants, local values, etc.

Actors, though, are not able to prevent data races by themselves when it comes to arbitrary memory referenced using UnsafeMutablePointer, global memory, and class references, which can be passed between actors thus also creating the possibility of data races.

Even with just the basic actor model, developers can reduce the chance of data races by means of global actors: if a framework requires all operations to take place on a specific queue, it can define a global actor, similar to UIActor, and specify it in its protocols.

To gain full actor isolation, though, Swift will need to add new features, among which the most important will be the ability to declare a type to be "actor local". This will make it impossible to pass that actor type between actors. When this is required, it will be possible to clone/unshare the actor before passing it.

At some point, those new features will be enforced by default, meaning all classes will be "actor local" and global variables will be required to be protected through a global actor or be declared "actor unsafe". While bringing concurrency safety at the heart of the language concurrency mechanisms, this will also mean backward source compatibility will break, although the Swift team hopes this will not be especially onerous.

The roadmap to making Swift a data race-free, concurrent language entails a number of language extensions that, given their complexity, will span across multiple releases, with many details of full actor isolation to be still finalized. InfoQ will keep reporting as new details become available.

Rate this Article

Adoption
Style

BT