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:
#include <chrono>
#include <thread>
#include <vector>
using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms);
}
int main() {
auto num_threads = 10;
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));
}
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
Example using ion::Barrier which synchronize all workers to the start of each iteration:
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
void Complete() noexcept {
std::cout << "Iteration beginning\n";
}
void Worker(Barrier& barrier, std::size_t num_iterations) {
for (auto count = 0u; count < num_iterations; ++count) {
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);
}
for (auto& thread : threads) {
thread.join();
}
}
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:
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
void Complete() noexcept {
std::cout << "Iteration beginning, invoked by the Supervisor\n";
}
void Supervisor(Barrier& barrier, std::size_t num_iterations) {
for (auto count = 0u; count < num_iterations; ++count) {
}
}
void Worker(Barrier& barrier, std::size_t num_iterations) {
for (auto count = 0u; count < num_iterations; ++count) {
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);
}
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>
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
Signals
See module.
#include <cstdint>
#include <iostream>
#include <thread>
int main() {
auto current = Wait(token);
std::cout << "Got signal: " << current << std::endl;
});
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.
#include <ion/chrono.hpp>
int main() {
using namespace std::chrono_literals;
ion::WaitFor(500us);
ion::WaitUntil(std::chrono::steady_clock::now() + 500us);
}