Breaking News

Introducing Swift Atomics

I’m delighted to announce Swift Atomics, a new open source package that enables direct use of low-level atomic operations in Swift code. The goal of this library is to enable intrepid systems programmers to start building synchronization constructs (such as concurrent data structures) directly in Swift.

Introducing Swift Atomics October 1, 2020 Karoy Lorentey is an engineer on the Swift Standard Library team at Apple.

As a quick taste, this is what atomic operations look like using this new package:

import Atomics import Dispatch let counter = ManagedAtomic < Int > ( 0 ) DispatchQueue . concurrentPerform ( iterations : 10 ) { _ in for _ in 0 ..< 1_000_000 { counter . wrappingIncrement ( by : 1 , ordering : . relaxed ) } } counter . load ( ordering : . relaxed ) // ⟹ 10_000_000

You may have noticed that the atomic operations in this example do not follow the exclusivity rules that govern normal Swift variables. Atomic operations may be performed from multiple concurrent threads of execution, so long as the value is only accessed via atomic operations.

This is enabled by SE-0282, a recently accepted Swift Evolution proposal that explicitly adopted a C/C++-style memory model for Swift, and (informally) described how regular Swift code interoperates with atomic operations. In fact, most APIs in this new package come from previous incarnations of the SE-0282 proposal: they were originally developed by an extremely productive collaborative effort on the Evolution forum. I am deeply grateful to all contributors to these discussions, and I hope the package will continue the collaboration in similarly high spirits!

Proceed at Your Own Risk

The Atomics package provides carefully considered API for atomic operations that follows established design principles for Swift APIs. However, the underlying operations work on a very low level of abstraction. Atomics – even more than other low-level concurrency constructs – are notoriously difficult to use correctly.

These APIs enable systems programming use cases that were previously out of reach for Swift programmers. In particular, atomics enable the creation of higher-level, easier-to-use constructs for managing concurrency without resorting to importing their implementation from another language.

Like unsafe APIs in the Standard Library, we recommend using this package very sparingly – preferably not at all! If it’s necessary, though, it is a good idea to:

Implement existing published algorithms rather than inventing new ones,

Isolate atomic code to small, easily reviewable units,

And avoid passing around atomic constructs as interface types.

Approach atomic code with extreme caution. Use copious amounts of Thread Sanitizer after every contact!

Supported Atomic Types

The package implements atomic operations for the following Swift types, all of which conform to the public AtomicValue protocol:

Standard signed integer types ( Int , Int64 , Int32 , Int16 , Int8 )

, , , , ) Standard unsigned integer types ( UInt , UInt64 , UInt32 , UInt16 , UInt8 )

, , , , ) Booleans ( Bool )

) Standard pointer types ( UnsafeRawPointer , UnsafeMutableRawPointer , UnsafePointer , UnsafeMutablePointer ), along with their optional-wrapped forms (such as Optional> )

, , , ), along with their optional-wrapped forms (such as ) Unmanaged references ( Unmanaged , Optional> )

, ) A special DoubleWord type that consists of two UInt values, low and high , providing double-wide atomic primitives

type that consists of two values, and , providing double-wide atomic primitives Any RawRepresentable type whose RawValue is in turn an atomic type (such as simple custom enum types)

type whose is in turn an atomic type (such as simple custom enum types) Strong references to class instances that opted into atomic use (by conforming to the AtomicReference protocol)

Of particular note is full support for atomic strong references. This provides a convenient memory reclamation solution for concurrent data structures that fits perfectly with Swift’s reference counting memory management model. (Atomic strong references are implemented in terms of DoubleWord operations.)

One common use case for an atomic strong reference is to create a lazily initialized (but otherwise constant) variable of some class type. Using general atomic references would be unreasonably expensive in this simple case, so we also provide a separate set of more efficient constructs ( ManagedAtomicLazyReference and UnsafeAtomicLazyReference ) that are optimized specifically for lazy initialization. This can be a useful replacement for lazy var stored properties in class contexts, which aren’t safe to use in concurrent contexts.

Memory Management

Atomic access is implemented in terms of dedicated atomic storage representations that are kept distinct from the corresponding regular (non-atomic) type. (E.g., the actual integer value underlying the counter above isn’t directly accessible.) This has several advantages:

it helps prevent accidental non-atomic access to atomic variables,

it enables certain atomic…

Apple Inc.

Read full article

Source link