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.
Loading...
Searching...
No Matches
jh::sync::ipc::process_counter< S, HighPriv > Class Template Referencefinal

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().

Detailed Description

template<jh::meta::TStr S, bool HighPriv = false>
requires (limits::valid_object_name<S, limits::max_name_length - 4>())
class jh::sync::ipc::process_counter< S, HighPriv >

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.

Design goals

  • Safe across processes and threads.
  • Consistent behavior between POSIX (shm_open) and Windows (CreateFileMapping).
  • Lock-protected read-modify-write semantics.

Read semantics

  • 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.

Write semantics

  • All modification methods (store, fetch_add, fetch_sub, fetch_apply) acquire the same mutex to ensure atomicity across processes.
  • Operations are linearized globally within the named counter scope.

Semantic clarification

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.

Internal synchronization objects

  • Main mutex: process_mutex<S + ".loc"> — protects all read-modify-write operations on the counter value.
  • Initialization mutex: 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.

Usage notes

  • Acts as a globally shared 64-bit counter.
  • All accesses within the same template literal S map to the same shared-memory region across all processes.
  • The counter is lazily initialized once per namespace; subsequent accesses reuse the same shared mapping.

Member Function Documentation

◆ fetch_add()

template<jh::meta::TStr S, bool HighPriv = false>
std::uint64_t jh::sync::ipc::process_counter< S, HighPriv >::fetch_add ( std::uint64_t delta = 1)
inlinenoexcept

Atomically add to the counter.

Increments the counter by delta under lock protection.

Parameters
deltaIncrement amount (default 1).
Returns
Previous counter value before addition.

◆ fetch_apply()

template<jh::meta::TStr S, bool HighPriv = false>
template<typename F>
std::uint64_t jh::sync::ipc::process_counter< S, HighPriv >::fetch_apply ( F && func)
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.

Template Parameters
FCallable type with signature uint64_t(uint64_t).
Parameters
funcTransformation function.
Returns
The previous counter value before transformation.

◆ fetch_sub()

template<jh::meta::TStr S, bool HighPriv = false>
std::uint64_t jh::sync::ipc::process_counter< S, HighPriv >::fetch_sub ( std::uint64_t delta = 1)
inlinenoexcept

Atomically subtract from the counter.

Decrements the counter by delta under lock protection.

Parameters
deltaDecrement amount (default 1).
Returns
Previous counter value before subtraction.

◆ load()

template<jh::meta::TStr S, bool HighPriv = false>
std::uint64_t jh::sync::ipc::process_counter< S, HighPriv >::load ( )
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.

Usage

  • Use in performance-sensitive paths where exact freshness is not required.
  • Typical use: reader-count check or approximate monitoring.
Returns
Current (possibly slightly outdated) counter value.

◆ load_force()

template<jh::meta::TStr S, bool HighPriv = false>
std::uint64_t jh::sync::ipc::process_counter< S, HighPriv >::load_force ( )
inlinenodiscardnoexcept

Locked and synchronized load.

Acquires the internal process-wide mutex before reading, guaranteeing full serialization with all concurrent writers.

Usage

  • Use for diagnostic or critical-path reads that must reflect an exact, globally synchronized value.
  • Typically used for invariant checks or transactional control logic.
Returns
The exact counter value after synchronization with all writers.

◆ load_strong()

template<jh::meta::TStr S, bool HighPriv = false>
std::uint64_t jh::sync::ipc::process_counter< S, HighPriv >::load_strong ( )
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.

Usage

  • Use when a transactionally accurate view is required.
  • Guarantees visibility order equivalent to std::atomic<T>::load(memory_order_seq_cst).
Returns
The most recent globally visible counter value.

◆ store()

template<jh::meta::TStr S, bool HighPriv = false>
void jh::sync::ipc::process_counter< S, HighPriv >::store ( std::uint64_t v)
inlinenoexcept

Atomically replace the counter value.

Acquires the internal lock, writes the new value, and enforces release and sequential consistency ordering.

Parameters
vNew value to store.

◆ unlink()

template<jh::meta::TStr S, bool HighPriv = false>
void jh::sync::ipc::process_counter< S, HighPriv >::unlink ( )
inlinestatic

Remove the counter's shared-memory object from the namespace (POSIX only).

Semantics

  • On POSIX systems, calls shm_unlink() for the counter name, and then delegates to process_mutex<S>::unlink() and process_mutex<S + ".loc">::unlink().
  • If the object does not exist (errno == ENOENT), the call is silently ignored.
  • If unlinking fails for other reasons, a std::runtime_error is thrown.

Idempotency

The operation is idempotent: calling unlink() multiple times is safe. Once removed, subsequent calls are treated as no-ops.

Windows

On Windows / MSYS2, there is no explicit unlink concept. Shared memory and synchronization handles are destroyed automatically when the last process closes them.


The documentation for this class was generated from the following file: