|
JH-Toolkit v1.4.1
An engineering-oriented C++20 toolkit with duck-typed concepts, static design, async coroutines, and semantic containers — header-only, RTTI-free, and concurrency-friendly.
|
Cross-process integer counter stored in shared memory (POSIX / Win32). More...
#include <jh/synchronous/ipc/process_counter.h>
Public Member Functions | |
| process_counter (const process_counter &)=delete | |
| process_counter & | operator= (const process_counter &)=delete |
| std::uint64_t | load () noexcept |
| Lightweight relaxed load. | |
| std::uint64_t | load_strong () noexcept |
| Strong, sequentially consistent load. | |
| std::uint64_t | load_force () noexcept |
| Locked and synchronized load. | |
| void | store (std::uint64_t v) noexcept |
| Atomically replace the counter value. | |
| std::uint64_t | fetch_add (std::uint64_t delta=1) noexcept |
| Atomically add to the counter. | |
| std::uint64_t | fetch_sub (std::uint64_t delta=1) noexcept |
| Atomically subtract from the counter. | |
| template<typename F> | |
| std::uint64_t | fetch_apply (F &&func) noexcept(noexcept(std::invoke(std::forward< F >(func), std::uint64_t{}))) |
| Apply a custom transformation atomically. | |
Static Public Member Functions | |
| static process_counter & | instance () |
| Singleton instance. | |
| static void | unlink () |
| Remove the counter's shared-memory object from the namespace (POSIX only). | |
| static void | unlink ()=delete |
| Disabled if HighPriv == false. Non-privileged variants cannot call unlink(). | |
Cross-process integer counter stored in shared memory (POSIX / Win32).
Provides process-visible integer storage with lock-protected modification. Uses OS-named shared memory (POSIX shm or Win32 file mapping) and a dedicated process_mutex<S + ".loc"> for consistency.
shm_open) and Windows (CreateFileMapping). load() — lightweight, relaxed read. Does not guarantee visibility of concurrent updates, but is sufficient when reading slightly stale values is harmless (e.g. in shared_process_mutex to check reader counts). load_strong() — sequentially consistent read (full memory fence). Suitable for transactional reads requiring visibility of all preceding writes in any process. load_force() — acquires the internal mutex before reading. Ensures full synchronization with all writers; used when strict atomicity or serialized state inspection is required. store, fetch_add, fetch_sub, fetch_apply) acquire the same mutex to ensure atomicity across processes. The API intentionally mimics std::atomic<uint64_t>, but this class is not an atomic type in the C++ memory model. On non-Linux systems, there is no standard facility for memory-mapped atomics; therefore, atomicity across processes must be provided explicitly via a lightweight inter-process mutex.
From a design standpoint, process_counter remains a primitive rather than a composite abstraction — it encapsulates synchronization internally and exposes a simple read-modify-write interface suitable for inter-process coordination.
process_mutex<S + ".loc"> — protects all read-modify-write operations on the counter value. process_mutex — guards the
one-time initialization of the shared memory region (ensures that
initialized flag and value are safely set by the first process that creates the object).
Both mutexes are created in the same namespace as the counter, which means
that if a user manually defines a process_mutex or
process_mutex<S + ".loc"> elsewhere, it will conflict with the internal synchronization of process_counter.
Therefore, avoid declaring any process_mutex with the same template literal S or its ".loc" suffix.
S map to the same shared-memory region across all processes.
|
inlinenoexcept |
Atomically add to the counter.
Increments the counter by delta under lock protection.
| delta | Increment amount (default 1). |
|
inlinenoexcept |
Apply a custom transformation atomically.
Executes func(old_value) under exclusive lock, replaces the stored value with the result, and returns the previous value.
| F | Callable type with signature uint64_t(uint64_t). |
| func | Transformation function. |
|
inlinenoexcept |
Atomically subtract from the counter.
Decrements the counter by delta under lock protection.
| delta | Decrement amount (default 1). |
|
inlinenodiscardnoexcept |
Lightweight relaxed load.
Performs an acquire-fenced read of the shared value. May return a slightly stale value if another process updates concurrently, but guarantees no tearing or partial reads.
|
inlinenodiscardnoexcept |
Locked and synchronized load.
Acquires the internal process-wide mutex before reading, guaranteeing full serialization with all concurrent writers.
|
inlinenodiscardnoexcept |
Strong, sequentially consistent load.
Performs a full sequential-consistency fence before reading. Ensures visibility of all preceding writes across processes that use the same shared memory region.
std::atomic<T>::load(memory_order_seq_cst).
|
inlinenoexcept |
Atomically replace the counter value.
Acquires the internal lock, writes the new value, and enforces release and sequential consistency ordering.
| v | New value to store. |
|
inlinestatic |
Remove the counter's shared-memory object from the namespace (POSIX only).
shm_unlink() for the counter name, and then delegates to process_mutex<S>::unlink() and process_mutex<S + ".loc">::unlink(). errno == ENOENT), the call is silently ignored. std::runtime_error is thrown. The operation is idempotent: calling unlink() multiple times is safe. Once removed, subsequent calls are treated as no-ops.
On Windows / MSYS2, there is no explicit unlink concept. Shared memory and synchronization handles are destroyed automatically when the last process closes them.