Top Related Projects
The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.
A microbenchmark support library
Abseil Common Libraries (C++)
An open-source C++ library developed and used at Facebook.
oneAPI Threading Building Blocks (oneTBB)
The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
Quick Overview
The swift-atomics
repository is a Swift package that provides a set of low-level atomic operations and types, which are essential for building concurrent and parallel systems. It is a part of the larger Swift project and is maintained by the Apple team.
Pros
- Performance: The atomic operations provided by this library are highly optimized for performance, making it suitable for use in performance-critical applications.
- Concurrency Support: The library enables developers to build concurrent and parallel systems in Swift, which is crucial for modern software development.
- Cross-Platform Compatibility: The
swift-atomics
library is designed to work across multiple platforms, including macOS, iOS, and Linux, making it a versatile choice for cross-platform development. - Alignment with Swift's Concurrency Model: The library's design aligns with Swift's concurrency model, making it a natural choice for developers working with Swift's built-in concurrency features.
Cons
- Limited Scope: The
swift-atomics
library is focused solely on providing low-level atomic operations and types, which may not be sufficient for all concurrency-related needs in a project. - Complexity: Working with low-level atomic operations can be complex and requires a deep understanding of concurrency concepts, which may not be suitable for all developers.
- Dependency on Swift: The library is tightly coupled with the Swift programming language, which means that it may not be suitable for projects that do not use Swift.
- Potential for Misuse: Improper use of atomic operations can lead to subtle bugs and race conditions, which can be difficult to diagnose and fix.
Code Examples
Here are a few examples of how to use the swift-atomics
library:
- Atomic Integer Increment:
import Atomics
var counter = AtomicInt(0)
counter.increment()
- Atomic Compare and Swap:
import Atomics
var value = AtomicInt(42)
let oldValue = value.compareExchange(expected: 42, desired: 43)
- Atomic Load and Store:
import Atomics
var flag = AtomicBool(false)
flag.store(true, ordering: .relaxed)
let currentValue = flag.load(ordering: .relaxed)
- Atomic Fence:
import Atomics
Atomics.fence(ordering: .sequentiallyConsistent)
Getting Started
To use the swift-atomics
library in your Swift project, you can add it as a dependency in your Package.swift
file:
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "MyProject",
dependencies: [
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.0")
],
targets: [
.target(
name: "MyProject",
dependencies: ["Atomics"])
]
)
Once you've added the dependency, you can import the Atomics
module and start using the provided atomic operations and types in your code.
Competitor Comparisons
The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.
Pros of cpprestsdk
- Provides a comprehensive set of features for building modern, asynchronous, and RESTful applications in C++.
- Supports a wide range of platforms, including Windows, Linux, macOS, and mobile platforms.
- Offers a rich set of utilities and tools for tasks like HTTP client/server, JSON/XML parsing, and more.
Cons of cpprestsdk
- Larger and more complex codebase compared to Swift-Atomics, which is a more focused library.
- May have a steeper learning curve for developers not familiar with the C++ ecosystem.
- Potentially higher maintenance overhead due to the broader set of features and supported platforms.
Code Comparison
Swift-Atomics:
import SwiftAtomics
var counter = AtomicInt(0)
counter.increment()
let value = counter.load()
cpprestsdk:
#include <cpprest/http_client.h>
pplx::task<web::http::response<web::json::value>> request() {
web::http::client::http_client client(U("https://api.example.com"));
return client.request(web::http::methods::GET);
}
A microbenchmark support library
Pros of google/benchmark
- Provides a comprehensive set of tools for benchmarking C++ code, including support for different types of benchmarks, statistical analysis, and output formatting.
- Allows for easy integration with existing test frameworks, making it a versatile tool for performance testing.
- Supports a wide range of platforms and compilers, ensuring compatibility across different development environments.
Cons of google/benchmark
- The library is primarily focused on C++ and may not be as well-suited for benchmarking other programming languages, such as Swift.
- The setup and configuration process can be more complex compared to simpler benchmarking tools, which may be a barrier for some users.
Code Comparison
Here's a brief comparison of the code structure between google/benchmark and apple/swift-atomics:
google/benchmark:
#include <benchmark/benchmark.h>
static void BM_StringCreation(benchmark::State& state) {
for (auto _ : state)
std::string empty_string;
}
BENCHMARK(BM_StringCreation);
BENCHMARK_MAIN();
apple/swift-atomics:
import SwiftAtomics
var counter = AtomicInt(0)
@inline(__always)
func increment() {
counter.increment()
}
@main
struct Main {
static func main() {
for _ in 0..<1_000_000 {
increment()
}
print(counter.load())
}
}
Abseil Common Libraries (C++)
Pros of Abseil
- Abseil provides a wide range of utility libraries, including data structures, algorithms, and synchronization primitives, beyond just atomic operations.
- The Abseil project has a larger community and more active development compared to Swift Atomics.
- Abseil is cross-platform and supports multiple programming languages, including C++ and Python.
Cons of Abseil
- The Abseil library is larger and more complex than the focused Swift Atomics library, which may be overkill for some use cases.
- The Abseil API is more verbose and less intuitive compared to the simple and straightforward API of Swift Atomics.
Code Comparison
Swift Atomics:
var value = AtomicInt(0)
value.load(ordering: .relaxed) // Load the value with relaxed ordering
value.store(42, ordering: .relaxed) // Store a new value with relaxed ordering
Abseil:
absl::Atomic<int> value(0);
value.load(std::memory_order_relaxed); // Load the value with relaxed ordering
value.store(42, std::memory_order_relaxed); // Store a new value with relaxed ordering
An open-source C++ library developed and used at Facebook.
Pros of Folly
- Folly is a comprehensive library that provides a wide range of utilities and data structures, while Swift Atomics is focused specifically on atomic operations.
- Folly has a large and active community, with contributions from many developers at Facebook.
- Folly includes a variety of concurrency primitives, such as futures and promises, which can be useful for building concurrent and asynchronous applications.
Cons of Folly
- Folly is a large and complex library, which may be overkill for projects that only need basic atomic operations.
- Folly is primarily written in C++, which may not be the preferred language for some Swift developers.
- Folly's API and documentation may be less familiar to Swift developers than the Swift Atomics library.
Code Comparison
Swift Atomics:
import SwiftAtomics
var counter = AtomicInt(0)
counter.increment()
let value = counter.load()
Folly:
#include <folly/AtomicIntrusiveLinkedList.h>
folly::AtomicIntrusiveLinkedList<MyNode> list;
list.push_front(new MyNode());
auto node = list.pop_front();
oneAPI Threading Building Blocks (oneTBB)
Pros of oneTBB
- Broader scope: Offers a comprehensive parallel programming library with various components beyond just atomics
- Cross-platform support: Works on multiple operating systems and architectures
- Mature and well-established: Has been in development for many years with a large user base
Cons of oneTBB
- Heavier footprint: Includes many features, which may be unnecessary for projects only needing atomic operations
- Steeper learning curve: Due to its extensive API and features, it may take longer to master
Code Comparison
Swift Atomics:
import Atomics
let atomic = ManagedAtomic<Int>(0)
atomic.store(42, ordering: .relaxed)
let value = atomic.load(ordering: .relaxed)
oneTBB:
#include <tbb/atomic.h>
tbb::atomic<int> atomic(0);
atomic.store(42);
int value = atomic.load();
Both libraries provide similar atomic operations, but Swift Atomics offers more explicit memory ordering options in its API.
The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
Pros of llvm/llvm-project
- Comprehensive collection of tools and libraries for compiler development, including the LLVM compiler infrastructure, Clang, and various other projects.
- Active community with a large number of contributors and a well-established development process.
- Supports a wide range of programming languages and architectures, making it a versatile choice for various projects.
Cons of llvm/llvm-project
- Larger and more complex than apple/swift-atomics, which may have a steeper learning curve for newcomers.
- Requires more resources (e.g., storage, build time) compared to a smaller, more focused project like apple/swift-atomics.
Code Comparison
apple/swift-atomics:
@inlinable
public func load(_ order: MemoryOrder = .sequential) -> Wrapped {
switch order {
case .relaxed:
return Atomic.loadRelaxed(&_value)
case .acquire:
return Atomic.loadAcquire(&_value)
case .release:
return Atomic.loadRelease(&_value)
case .sequential:
return Atomic.load(&_value)
}
}
llvm/llvm-project:
template <typename T>
T AtomicLoad(const T *Ptr, std::memory_order Order) {
switch (Order) {
case std::memory_order_relaxed:
return __c11_atomic_load((_Atomic(T) *)Ptr, std::memory_order_relaxed);
case std::memory_order_consume:
return __c11_atomic_load((_Atomic(T) *)Ptr, std::memory_order_consume);
case std::memory_order_acquire:
return __c11_atomic_load((_Atomic(T) *)Ptr, std::memory_order_acquire);
case std::memory_order_seq_cst:
return __c11_atomic_load((_Atomic(T) *)Ptr, std::memory_order_seq_cst);
}
llvm_unreachable("Invalid memory order");
}
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Swift Atomics âï¸ï¸
This package implements an atomics library for Swift, providing atomic operations for a variety of Swift types, including integers and pointer values. The goal is to enable intrepid developers to start building synchronization constructs directly in Swift.
Atomic operations aren't subject to the usual exclusivity rules. The same memory location may be safely read and updated from multiple concurrent threads of execution, as long as all such access is done through atomic operations. For example, here is a trivial atomic counter:
import Atomics
import Dispatch
let counter = ManagedAtomic<Int>(0)
DispatchQueue.concurrentPerform(iterations: 10) { _ in
for _ in 0 ..< 1_000_000 {
counter.wrappingIncrement(ordering: .relaxed)
}
}
counter.load(ordering: .relaxed) // â¹ 10_000_000
The only way to access the counter value is to use one of the methods provided by ManagedAtomic
, each of which implement a particular atomic operation, and each of which require an explicit ordering value. (Swift supports a subset of the C/C++ memory orderings.)
Table of Contents
Word of Warning
Atomic values are fundamental to managing concurrency. However, they are far too low level to be used lightly. These things are full of traps. They are extremely difficult to use correctly -- far trickier than, say, unsafe pointers.
The best way to deal with atomics is to avoid directly using them. It's always better to rely on higher-level constructs, whenever possible.
This package exists to support the few cases where the use of atomics is unavoidable -- such as when implementing those high-level synchronization/concurrency constructs.
The primary focus is to provide systems programmers access to atomic operations with an API design that emphasizes clarity over superficial convenience:
-
Each atomic operation is invoked in client code using a clear, unabbreviated name that directly specifies what that operation does. Atomic operations are never implicit -- they are always clearly spelled out.
-
There is no default memory ordering, to avoid accidental (and costly) use of sequential consistency. (This is a surprisingly common issue in C/C++.)
-
Operations such as compare/exchange prefer to keep input values cleanly separated from results. There are no
inout
parameters.
Getting Started
To use Atomics
in your own project, you need to set it up as a package dependency:
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "MyPackage",
dependencies: [
.package(
url: "https://github.com/apple/swift-atomics.git",
.upToNextMajor(from: "1.2.0") // or `.upToNextMinor
)
],
targets: [
.target(
name: "MyTarget",
dependencies: [
.product(name: "Atomics", package: "swift-atomics")
]
)
]
)
Compatibility
Swift Releases
The atomics implementation is inherently far more tightly coupled to the compiler than usual, so tagged versions of this package typically only support a narrow range of Swift releases:
swift-atomics | Supported Swift Versions |
---|---|
1.0.x | 5.3, 5.4, 5.5 |
1.1.x | 5.6, 5.7, 5.8 |
1.2.x | 5.7, 5.8, 5.9 |
main | 5.8, 5.9, unreleased snapshots of 5.10 and trunk |
Note that the main
branch is not a tagged release, so its contents are inherently unstable. Because of this, we do not recommended using it in production. (For example, its set of supported Swift releases is subject to change without notice.)
Source Stability
The Swift Atomics package is source stable. The version numbers follow Semantic Versioning -- source breaking changes to public API can only land in a new major version.
The public API of current versions of the swift-atomics
package consists of non-underscored declarations that are marked public
in the Atomics
module.
By "underscored declarations" we mean declarations that have a leading underscore anywhere in their fully qualified name. For instance, here are some names that wouldn't be considered part of the public API, even if they were technically marked public:
FooModule.Bar._someMember(value:)
(underscored member)FooModule._Bar.someMember
(underscored type)_FooModule.Bar
(underscored module)FooModule.Bar.init(_value:)
(underscored initializer)
Interfaces that aren't part of the public API may continue to change in any release, including patch releases.
Note that contents of the _AtomicsShims
module explicitly aren't public API. (As implied by its underscored module name.) The definitions therein may therefore change at whim, and the entire module may be removed in any new release -- do not import this module directly. We also don't make any source compatibility promises about the contents of the Utilities
, Tests
, Xcode
and cmake
subdirectories.
If you have a use case that requires using underscored APIs, please submit a Feature Request describing it! We'd like the public interface to be as useful as possible -- although preferably without compromising safety or limiting future evolution.
Future minor versions of the package may introduce changes to these rules as needed.
We'd like this package to quickly embrace Swift language and toolchain improvements that are relevant to its mandate. Accordingly, from time to time, new versions of this package will require clients to upgrade to a more recent Swift toolchain release. (This allows the package to make use of new language/stdlib features, build on compiler bug fixes, and adopt new package manager functionality as soon as they are available.)
Requiring a new Swift release will only require a minor version bump.
Features
For a detailed overview of the interfaces offered by this package, please see our API documentation.
The package implements atomic operations for the following Swift constructs, 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<T>
,UnsafeMutablePointer<T>
), along with their optional-wrapped forms (such asOptional<UnsafePointer<T>>
) - Unmanaged references (
Unmanaged<T>
,Optional<Unmanaged<T>>
) - A special
DoubleWord
type that consists of twoUInt
values,first
andsecond
, providing double-wide atomic primitives - Any
RawRepresentable
type whoseRawValue
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.) However, accessing an atomic strong reference is (relatively) expensive, so we also provide a separate set of efficient constructs (ManagedAtomicLazyReference
and UnsafeAtomicLazyReference
) for the common case of a lazily initialized (but otherwise constant) atomic strong reference.
Lock-Free vs Wait-Free Operations
All atomic operations exposed by this package are guaranteed to have lock-free implementations. However, we do not guarantee wait-free operation -- depending on the capabilities of the target platform, some of the exposed operations may be implemented by compare-and-exchange loops. That said, all atomic operations map directly to dedicated CPU instructions where available -- to the extent supported by llvm & Clang.
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 custom storage representations (such as the one used by atomic strong references), and
- it is a better fit with the standard C atomics library that we use to implement the actual operations (as enabled by SE-0282).
While the underlying pointer-based atomic operations are exposed as static methods on the corresponding AtomicStorage
types, we strongly recommend the use of higher-level atomic wrappers to manage the details of preparing/disposing atomic storage. This version of the library provides two wrapper types:
- an easy to use, memory-safe
ManagedAtomic<T>
generic class and - a less convenient, but more flexible
UnsafeAtomic<T>
generic struct.
Both constructs provide the following operations on all AtomicValue
types:
func load(ordering: AtomicLoadOrdering) -> Value
func store(_ desired: Value, ordering: AtomicStoreOrdering)
func exchange(_ desired: Value, ordering: AtomicUpdateOrdering) -> Value
func compareExchange(
expected: Value,
desired: Value,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value)
func compareExchange(
expected: Value,
desired: Value,
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value)
func weakCompareExchange(
expected: Value,
desired: Value,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value)
func weakCompareExchange(
expected: Value,
desired: Value,
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value)
Integer types come with additional atomic operations for incrementing or decrementing values and bitwise logical operations.
func loadThenWrappingIncrement(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenWrappingDecrement(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenBitwiseAnd(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenBitwiseOr(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenBitwiseXor(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func wrappingIncrementThenLoad(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) -> Value
func wrappingDecrementThenLoad(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) -> Value
func bitwiseAndThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func bitwiseOrThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func bitwiseXorThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func wrappingIncrement(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
)
func wrappingDecrement(
by operand: Value = 1,
ordering: AtomicUpdateOrdering
)
Bool
provides select additional boolean operations along the same vein.
func loadThenLogicalAnd(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenLogicalOr(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func loadThenLogicalXor(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func logicalAndThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func logicalOrThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
func logicalXorThenLoad(
with operand: Value,
ordering: AtomicUpdateOrdering
) -> Value
For an introduction to the APIs provided by this package, for now please see the first version of SE-0282.
The current version of the Atomics
module does not implement APIs for tagged atomics (see issue #1), although it does expose a DoubleWord
type that can be used to implement them. (Atomic strong references are already implemented in terms of DoubleWord
, although in their current form they do not expose any user-customizable bits.)
Contributing to Swift Atomics
Swift Atomics is a standalone library separate from the core Swift project. We expect some of the atomics APIs may eventually get incorporated into the Swift Standard Library. If and when that happens such changes will be proposed to the Swift Standard Library using the established evolution process of the Swift project.
This library is licensed under the Swift License. For more information, see the Swift.org Community Guidelines, Contribution Guidelines, as well as the files LICENSE.txt, CONTRIBUTING.md and CODE_OF_CONDUCT.md at the root of this repository.
Swift Atomics uses GitHub issues to track bugs and enhancement requests. We use pull requests for development.
We have a dedicated Swift Atomics Forum where people can ask and answer questions on how to use or work on this package. It's also a great place to discuss its evolution.
If you find something that looks like a bug, please open a Bug Report! Fill out as many details as you can.
To fix a small issue or make a tiny improvement, simply submit a PR with the changes you want to make. If there is an existing issue for the bug you're fixing, please include a reference to it. Make sure to add tests covering whatever changes you are making.
For larger feature additions, it's a good idea to discuss your idea in a new Feature Request or on the forum before starting to work on it. If the discussions indicate the feature would be desirable, submit the implementation in a PR, and participate in its review discussion.
Development
This package defines a large number of similar-but-not-quite-the-same operations. To make it easier to maintain these, we use code generation to produce them.
A number of source files have a .swift.gyb
extension. These are using a Python-based code generation utility called gyb which we also use within the Swift Standard Library (the name is short for Generate Your Boilerplate). To make sure the package remains buildable by SPM, the autogenerated output files are committed into this repository. You should not edit the contents of autogenerated
subdirectories, or your changes will get overwritten the next time the code is regenerated.
To regenerate sources (and to update the inventory of XCTest tests), you need to manually run the script generate-sources.sh
in the Utilities folder of this repository. This needs to be done every time you modify one of the template files.
In addition to gyb, the _AtomicsShims.h
header file uses the C preprocessor to define trivial wrapper functions for every supported atomic operation -- memory ordering pairing.
âï¸ï¸
Top Related Projects
The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.
A microbenchmark support library
Abseil Common Libraries (C++)
An open-source C++ library developed and used at Facebook.
oneAPI Threading Building Blocks (oneTBB)
The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot