ion 0.12.0
Atomic concurrency support library
Loading...
Searching...
No Matches
README

ion

ion is a C++23 atomic concurrency support library. It has no other dependencies than the C++ standard library.

Warning
Library API is unstable and is subject to change.

Build, Install and Use

Use waf to build run unit tests and install

waf configure build test install --mode=release

To use in a wtools project import pkg-config dependency ion or wdep dependency ion.ion, e.g.:

from wtools.project import declare_project
def configure(cnf):
cnf.check_cfg(package="ion", uselib_store="ION", args="--cflags --libs")
declare_project(
"my-project",
"0.1.0",
recurse="lib",
requires="cxx",
cxx=dict(cxx_std="c++17"),
)

Sanitizers

ion::Barrier use std::atomic_thread_fence which is not supported by thread sanitizer TSAN (-fsanitize=thread). However, false positivies from this avoided with additional acquire/release annotations when compiling with thread sanitizer. Nevertheless you may still get the -Wtsan warning that std::atomic_thread_fence is not supported by TSAN because it is unsupported to suppress the warning using ion using pragma diagnostics.

Motivation

C++ standard library have many nice synchronization primitives like std::latch or std::barrier, but these are not always suitable in a realtime or low-latency environment where you don't want to make syscalls or yield the thread. As such this library provides a set of such primitives that is guaranteed to not make syscalls or yield.

Components

ion provide

Barriers

See module, std::latch. and std::barrier for additional documentation.

Example using ion::Latch to synchronize start of a set of workers:

/**
* @file
* @copyright
* SPDX-FileCopyrightText: 2023-2023 European Southern Observatory (ESO)
*
* SPDX-License-Identifier: LGPL-3.0-only
*/
#include <chrono>
#include <thread>
#include <vector>
#include <ion/latch.hpp>
void Worker(ion::Latch& barrier) {
// Wait for all workers to be ready
barrier.ArriveAndWait();
// Fake some work
using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms);
}
int main() {
auto num_threads = 10;
ion::Latch start(num_threads);
std::vector<std::thread> threads;
threads.reserve(num_threads);
for (auto thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
threads.emplace_back(&Worker, std::ref(start));
}
// Let it complete and then join
for (auto& thread : threads) {
thread.join();
}
}
Single use thread-coordination mechanism modelled after std::latch.
Definition latch.hpp:45
void ArriveAndWait(std::ptrdiff_t n=1) noexcept
Decrements expected count by n and waits.
Definition latch.hpp:139
ion::Latch

Example using ion::Barrier which synchronize all workers to the start of each iteration:

/**
* @file
* @copyright
* SPDX-FileCopyrightText: 2023-2023 European Southern Observatory (ESO)
*
* SPDX-License-Identifier: LGPL-3.0-only
*/
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#include <ion/barrier.hpp>
/** Completion function */
void Complete() noexcept {
std::cout << "Iteration beginning\n";
}
using Barrier = ion::Barrier<void (*)() noexcept>;
void Worker(Barrier& barrier, std::size_t num_iterations) {
for (auto count = 0u; count < num_iterations; ++count) {
// Synchronize start of each iteration
barrier.ArriveAndWait();
// Fake some work
using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms);
}
}
int main() {
auto const num_threads = 10;
auto const num_iterations = 100;
Barrier barrier(num_threads, &Complete);
std::vector<std::thread> threads;
threads.reserve(num_threads);
for (auto thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
threads.emplace_back(&Worker, std::ref(barrier), num_iterations);
}
// Let it complete and then join
for (auto& thread : threads) {
thread.join();
}
}
ion::Barrier
Reusable thread-coordination mechanism modelled after std::barrier.
Definition barrier.hpp:90
void ArriveAndWait(std::ptrdiff_t n=1) noexcept
Equivalent to barrier.Wait(barrier.Arrive(n))
Definition barrier.hpp:391

Example using ion::FlexBarrier which is similar to previous example but ensures that completion function is only invoked by the supervisor:

/**
* @file
* @copyright
* SPDX-FileCopyrightText: 2023-2023 European Southern Observatory (ESO)
*
* SPDX-License-Identifier: LGPL-3.0-only
*/
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#include <ion/barrier.hpp>
/** Completion function */
void Complete() noexcept {
std::cout << "Iteration beginning, invoked by the Supervisor\n";
}
using Barrier = ion::FlexBarrier<void (*)() noexcept>;
void Supervisor(Barrier& barrier, std::size_t num_iterations) {
for (auto count = 0u; count < num_iterations; ++count) {
// Synchronize start of each iteration
}
}
void Worker(Barrier& barrier, std::size_t num_iterations) {
for (auto count = 0u; count < num_iterations; ++count) {
// Synchronize start of each iteration
// Fake some work
using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms);
}
}
int main() {
auto const num_threads = 10;
auto const num_iterations = 100;
Barrier barrier(num_threads, &Complete);
std::vector<std::thread> threads;
threads.reserve(num_threads);
threads.emplace_back(&Supervisor, std::ref(barrier), num_iterations);
for (auto thread_idx = 1; thread_idx < num_threads; ++thread_idx) {
threads.emplace_back(&Worker, std::ref(barrier), num_iterations);
}
// Let it complete and then join
for (auto& thread : threads) {
thread.join();
}
}
Reusable thread-coordination mechanism similar to ion::Barrier but allows control where the completio...
Definition barrier.hpp:229
@ Allowed
Completion function invocation is allowed.
Definition barrier.hpp:202
@ Disallowed
Completion function invocation is not allowed.
Definition barrier.hpp:207

Possible uses for the completion function include:

  • Await input data.
  • Distribute work to workers.
  • Aggregate and yield result from prior iteration.

Locks

See module.

#include <thread>
#include <mutex>
#include <iostream>
#include <ion/spinLock.hpp>
int main() {
std::string msg;
auto work = [&] {
std::lock_guard g(mtx);
msg += "hello ";
};
std::thread t1(work);
std::thread t2(work);
t1.join();
t2.join();
std::cout << msg;
}
Busy spinning lock that satisfies BasicLockable, Lockable and Mutex requirements.
Definition spinLock.hpp:47
ion::SpinLock

Signals

See module.

/**
* @file
* @copyright
* SPDX-FileCopyrightText: 2023-2023 European Southern Observatory (ESO)
*
* SPDX-License-Identifier: LGPL-3.0-only
*/
#include <cstdint>
#include <iostream>
#include <thread>
#include <ion/signal.hpp>
int main() {
auto source = ion::SignalSource<int>();
// To avoid race-condition between storing new signal value and what token
// initializes to we create the token in the main thread.
std::thread thread([token = ion::SignalToken<int>(&source)]() mutable {
auto current = Wait(token);
std::cout << "Got signal: " << current << std::endl;
});
// Update signal
source.Store(42);
thread.join();
}
Atomic signal source.
Definition signal.hpp:66
Signal token that has an associated SignalSource and last known value.
Definition signal.hpp:187
ion::{SignalSource, SignalToken} and algorithms

Time Utilities

This includes simple utilities for busy-waiting.

See module.

/**
* @file
* @copyright
* SPDX-FileCopyrightText: 2025-2025 European Southern Observatory (ESO)
*
* SPDX-License-Identifier: LGPL-3.0-only
*/
#include <ion/chrono.hpp>
int main() {
using namespace std::chrono_literals;
// Busy-wait for 500us
ion::WaitFor(500us);
// Which is equivalent to using steady clock with 500us time in the future.
ion::WaitUntil(std::chrono::steady_clock::now() + 500us);
}