Programming language: Swift 5.5 turns the parallelism of language on its head


Apple’s open source programming language Swift has been released in version 5.5 and brings numerous innovations for parallel programming. In addition to the async / await pattern, the publication offers structured concurrency, asynchronous sequences and the actor model.

Version 5.4, published five months ago, already had initial preparations for the new concurrency model on board, which are now being incorporated into numerous proposals in Swift. This proposal SE-0296 introduces the Async / Await pattern to implement coroutines analogous to programming languages ​​such as C #, Kotlin, C ++ 20 or JavaScript.

The key word async declares a function as asynchronous and calls it with the keyword await. The function can interrupt its execution at any time, for example in order not to block the execution of the program while waiting for incoming data in the case of network transactions.

The compiler takes care of the implementation so that developers can write asynchronous code without additional checking routines analogous to synchronous code, as in the following example from the proposal:

func loadWebResource(_ path: String) async throws -> Resource
 decodeImage(_ r1: Resource, _ r2: Resource) async throws -> Image
func dewarpAndCleanupImage(_ i : Image) async throws -> Image

func processImageData() async throws -> Image {
  let dataResource  = try await loadWebResource("dataprofile.txt")
  let imageResource = try await loadWebResource("imagedata.dat")
  let imageTmp      = 
    try await decodeImage(dataResource, imageResource)
  let imageResult   = try await dewarpAndCleanupImage(imageTmp)
  return imageResult

The actual implementation for concurrent programs can be found in another innovation of Swift 5.5: SE-0304 introduces structured parallelism. It enables the division of sequential processes into parallel structures. As an illustrative example, the proposal brings the preparation of dinner on the table, in which some steps can be parallelized while others have to wait for each other. So someone can marinate the meat while someone else cuts the vegetables. At the latest when serving, all tasks have to come together.

Tasks form the basis for parallel processing. As suggested, in Swift 5.5 a task behaves to an asynchronous function as a thread behaves to a synchronous function. Every task has one of three states: it is running, suspended, or completed. the TaskPriority sets the priority of the respective task as high, medium, low or background Fixed. Alternatively, you can use the Apple-specific layers userInitiated to the high and utility to the low use.

One TaskGroup can combine several tasks into sub-tasks. The group does not finish their work and returns until all of the children have completed their execution. The children inherit the priority of their parents. Apart from the regular execution, there may be an abort: The call to cancel() manually cancels the task and an unexpected error in a parent task leads to the cancellation of incomplete child tasks.

Additional methods enable a task to be controlled: With sleep() he pauses for a certain time, and suspend() stops it explicitly to give time to other tasks.

In addition, this leads to proposal SE-0298 AsyncSequence– a protocol that is essentially synchronous Sequence– corresponds to the protocol, but is designed for values ​​that take time to arrive. Because of the asynchronous processing, the next()Function within the AsyncSequence how async be defined. Iterating over the sequence is for example with for try await n in MySequence() possible.

Another proposal forms the bridge to simultaneous applications in Objective-C: SE-0297 defines the translation of completion handlers from Objective-C into asyncMethods in Swift. Conversely, asynchronous methods can be used in the latter programming language @objc provide them as calls to Objective-C and pass them as completion handler methods.

This SE-0306 proposal finally introduces the actor model into Swift. The suggestion came soon after Swift 5.3 was released. It represents a concept to avoid typical pitfalls of parallel programming such as data races. In the model, the actuators behave similarly to classes, but protect the variable values ​​from external access. Messages replace direct function calls, while direct access only consists of. consists self allowed is. The compiler takes care of the separation of the actuators.

The actuator takes care of the processing as soon as this is possible without possible race conditions. Each actor has their own mailbox or message queue that receives all messages until they can process them. The actuators each have their own inbox and process the messages sequentially. To avoid race conditions, there is no parallel processing of actor-isolated code.

In concurrent programming, race conditions are a typical problem of nondeterministic behavior: if, for example, two threads running in parallel perform calculations on a global variable, errors can occur, but they do not always occur. For the simple case that both threads read the value first, then multiply by 2 and finally write it back to the same variable, the correct case gives a result of 4 for the output value 1.

The prerequisite for this is that a thread first reads the variable with the value 1, multiplies it by 2 and then writes the result 2, before the second thread reads this 2, multiplies it again by 2 and then writes a 4 when the second thread writes the value reads while the first thread has already read the result but not yet written it, both calculate with the initial value 1 and then write the 2 back to the variable as the wrong end result.

Data races describe that memory access in one thread by a write operation to the same memory area in another thread is potentially dangerous or disruptive for the correct program flow. In some definitions they are part of the racing conditions while others see them as a problem of their own.

As an additional extension, SE-0316 introduces Global Actors, where the actors are available directly from the main thread of the program.

Swift 5.5 brings some other innovations with it, some of which extend the new parallelism model, such as Task Local Values ​​(SE-0311), async let-Bindings (SE-0317), Effective Read-Only Properties (SE-0310) or AsyncStream and AsyncThrowingStream (SE-0314).

In addition to the concurrency, the expansion of the property wrappers introduced in Swift 5.1 to include parameters of functions and closures (SE-0293) and the Codable Synthesis for Enums with Associated Values ​​(SE-0295) is noteworthy. In addition, Swift’s Package Manager now allows a curated list of packages and associated metadata to be created as a package collection (SE-0291).

The full list of what’s new can be found on the Swift blog. You can find binary files of the open source programming language on the download page. Another page refers to the individual GitHub repositories with the source code.



Comments are closed.