Swiftpack.co - Package - glessard/swift-atomics

swift-atomics Build Status

Some atomic functions made available to Swift 3.1 and up, thanks to Clang

The atomic functions available in /usr/include/libkern/OSAtomic.h are quite limiting in Swift, due to impedance mismatches between the type systems of Swift and C. Furthermore, some simple things such as a synchronized load or a synchronized store are not immediately available. On top of that, they have now been deprecated.

Clang, of course, implements the C11 atomic functions — and they're available on Linux.

This project bridges a subset of Clang's C11 atomics support to Swift, as two modules.

Module SwiftAtomics

SwiftAtomics has a swift-style interface to provide access to atomic operations. SwiftAtomics implements the following types:

  • AtomicPointer, AtomicMutablePointer, AtomicRawPointer, AtomicMutableRawPointer and AtomicOpaquePointer;
  • AtomicInt and AtomicUInt, as well as signed and unsigned versions of the 8-bit, 16-bit, 32-bit and 64-bit integer types;
  • AtomicBool

The pointer types have the following methods:

  • load, store, swap, and CAS

The integer types have the following methods:

  • load, store, swap, CAS, add, subtract, increment, decrement, bitwiseAnd, bitwiseOr, and bitwiseXor

AtomicBool has the following methods:

  • load, store, swap, CAS, and, or, and xor.

The memory ordering (from <stdatomic.h>) can be set by using the order parameter on each method; the defaults are .acquire for loading operations, .release for storing operations, and .acqrel for read-modify-write operations. Note that memory_order_consume has no equivalent in this module, as (as far as I can tell) clang silently upgrades that ordering to memory_order_acquire, making it impossible (at the moment) to test whether an algorithm can properly use memory_order_consume. This also means nothing is lost by its absence.

The integer types have a value property, as a convenient way to perform a .relaxed load. The pointer types have a pointer property, which performs an .acquire load.

Module CAtomics

The second module is CAtomics, which provides atomics to Swift using a C-style interface. SwiftAtomics is built on top of CAtomics. The types implemented in CAtomics are the same as in SwiftAtomics, minus those which require Swift's generic features. Functions defined in CAtomics are prefixed with CAtomics; they are Load, Store, Exchange, and CompareAndExchange for all types. The integer types add Add, Subtract, BitwiseAnd, BitWiseOr, and BitWiseXor; AtomicBool adds And, Or, and Xor.

Notes on atomics and the law-of-exclusivity:

My experimentation has shown that the types defined in SwiftAtomics are compatible with Swift 5's run-time exclusivity checking when used as members of class instances, but present difficulties when the thread sanitizer is enabled.

Atomic types are useful as synchronization points between threads, and therefore have an interesting relationship with Swift's exclusivity checking. They should be used as members of reference types, or directly captured by closures. They are struct types, so as to be not incur additional memory allocation, but that feature means that if you use the thread sanitizer, it will warn about them.

In order to use atomics in a way that is acceptable to the thread sanitizer, one must have allocated memory for atomic variables on the heap using UnsafeMutablePointer. Then, pass that pointer to the functions defined in the CAtomics module, as needed. I haven't found a way to use the swift-style wrappers in a way that doesn't trigger the thread sanitizer.

import CAtomics

class Example {
  private var counter = UnsafeMutablePointer<AtomicInt>.allocate(capacity: 1)
  init() {
    CAtomicsInitialize(counter, 0)
  }

  deinit {
    counter.deallocate()
  }

  func increment(by value: Int = 1) {
    CAtomicsAdd(counter, value, .relaxed)
  }
}

Requirements

This library requires Swift 3.1 or later. On Linux, it also requires Clang 3.6 or later.

Github

link
Stars: 109

Dependencies

Used By

Total: 0

Releases

Tweaks to compare-and-exchange functions - 2020-04-21 20:11:53

Move choice between weak and strong CAS out of the C parameter list in CAtomics

  • In the CAtomics module, all types have direct CompareAndExchangeWeak and CompareAndExchangeStrong functions rather than a single function with a parameter selecting between strong and weak.

  • The old version of CompareAndExchange with a CASType parameter still exists, but forwards to either of the other two, making it objectively worse.

  • SwiftAtomics internally adapts to the above, but is otherwise unchanged.

Fix for Windows - 2020-04-21 20:04:19

  • use intptr_t in tagged pointer unions.

Simplifications, mostly. - 2019-12-05 00:19:16

  • Changes the default memory ordering parameters in SwiftAtomics. The defaults are now .acquire for loading operations, .release for storing operations, and .acqrel for read-modify-write operations.

  • Removes the load() function on AtomicReference. load required locking, and it failed once every several million attempts (under contention), and that just wasn't good enough. (It fails seldom enough that what I thought had been extensive testing prior to version 4.2.0 was in fact insufficient.) There's been a fair bit of trying to fix it in the last year, but nothing has completely worked. The correct solution may require collaboration with Swift's ARC reference-counting runtime, and therefore will have to wait until a similar type appears in the standard library.

Version 5 - 2019-05-07 16:53:07

This release has major changes to the CAtomics package, in that it no longer uses swift-style function names, but uses (once more) C-style names: it switches from using the SWIFT_NAME macro to __attribute__((overloadable)). This allows all the necessary changes to have a similar shape, as AtomicInt.load(_ order: LoadMemoryOrder) becomes CAtomicsLoad(_ atomic: UnsafeMutablePointer<AtomicInt>, order: LoadMemoryOrder).

The Swift-style module is renamed to SwiftAtomics, which is less presumptuous than just Atomics. (Still presumptuous, but the standard library is unlikely to ever have a module named SwiftAtomics.)

Finally, tagged pointers are now usable from the SwiftAtomics module, after having been usable from CAtomics since 4.4.0.

More Cleanup - 2019-05-03 23:17:21

final tweaks?

Fix bug in `AtomicReference.load()` - 2019-05-01 21:43:31

With some – but not all – versions of the Swift compiler, the test testLoadVsDeinit() would occasionally fail. This happened with compiler 3.1.1 (sometimes), and with compiler 5.0 in compatibility modes (sometimes). The latter being a substantial problem. Inserting an explicit memory fence appears to have fixed the problem, as the test was able to run for about 6 hours on a 4-simultaneous-thread machine, in a configuration that had previously exhibited the issue.

Some Cleanup - 2019-05-01 21:33:00

Cleaned up some unnecessary code and some inconsistencies with discardable result annotations.

Removed the padded types, because they're just not that useful. If padding is necessary, heap allocation is possible, and the programmer can precisely control the layout of heap-allocated data, thanks to the RawPointer family. For example: allocate N+128 bytes and use the middle N bytes for the variable. This way the variable is guaranteed to be by itself on its own cache line. If the cache line is 64 bytes, that is.

Rename module to SwiftAtomics - 2019-04-12 00:39:06

The original version of this module was an experiment, yet the name of the module remained since then. It shouldn't have. Hopefully the standard library will have its own Atomics submodule one day, and it should have the name it wants. This new name is plenty presumptuous enough.

Add tagged pointers - 2019-04-05 01:07:29

Add atomic tagged pointer types that take advantage of 128-bit atomics on 64-bit platforms. This requires compiling with the -mcx16 flag for Clang on Linux.

CAtomics package update - 2019-04-05 01:04:47

There are no changes to module Atomics

Changed the style of function calls to the underlying module CAtomics, from swift-like to c-like. The swift-like versions couldn't be used in a way that avoided triggering the thread sanitizer, whereas c-style functions can.

The new function names are overloaded thanks to the overloadable Clang attribute; given that the names don't change according to the type of the first parameter, this makes migration somewhat less painful for anyone who has to do it. The changes are mostly one-for-one. Namely, AtomicInt.load(_ order: LoadMemoryOrder) becomes CAtomicsLoad(_ atomic: UnsafeMutablePointer<AtomicInt>, order: LoadMemoryOrder)

One of the swift-style functions remains for each type, namely an initializer of the form public init(_ value: <non-atomic-type>) that allows for initialization of the form let a = AtomicInt(42).

Fix for Swift 5 compiler - 2019-02-12 19:56:13

  • The Swift 5 compiler no longer imports structs with large alignments. This defeats the approach I previously took to pad structs.
  • In order to get padded structs to mitigate false sharing effects, the package now inserts explicit padding rather than explicitly requesting a large alignment. This is not the ideal solution, but it's good enough for most cases.
  • The padded types are renamed from 4.4.0, with abject apologies to anyone who might be affected. I should have tried building with Swift 5 a tad earlier.

4.4.0 - 2019-02-09 00:10:30

  • Adds tagged pointers to the CAtomics package, taking advantage of 128-bit atomic instructions in x64 and ARM64
  • Adds cache-aligned variants to various types from CAtomics. These types take an entire cache line to themselves, which can be useful in some cases.
  • Adds initializers, such as AtomicInt32.init(_ value: Int32) and AtomicRawPointer.init(_ pointer: UnsafeRawPointer). Previously one had to use a default initializer followed by a call to the initialize method.
  • Adds a compare-and-swap operation to AtomicReference
  • Supports the Windows build of Swift

Improvements for Swift 4.2 - 2018-08-31 17:26:27

Improvements for Swift 4.1 - 2018-07-18 20:28:10

Improved CAtomics - 2017-09-27 08:15:48

The underlying C package has a nicer interface when used from Swift.