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_mutex< S, HighPriv > Class Template Referencefinal

Cross-platform named process-wide mutex primitive. More...

#include <jh/synchronous/ipc/process_mutex.h>

Public Member Functions

 process_mutex (const process_mutex &)=delete
 Deleted copy constructor.
process_mutexoperator= (const process_mutex &)=delete
 Deleted copy assignment.
 process_mutex (process_mutex &&)=delete
 Deleted move constructor.
process_mutexoperator= (process_mutex &&)=delete
 Deleted move assignment.
void lock ()
 Acquire the lock (blocking).
bool try_lock ()
 Try acquire without blocking.
template<typename Rep, typename Period>
bool try_lock_for (const std::chrono::duration< Rep, Period > &d)
 Attempt to acquire the lock, waiting for a maximum duration.
template<typename Clock, typename Duration>
bool try_lock_until (const std::chrono::time_point< Clock, Duration > &tp)
 Attempt to acquire the lock until an absolute time point.
void unlock ()
 Release the lock.

Static Public Member Functions

static constexpr const char * name () noexcept
 Get OS-visible name.
static process_mutexinstance ()
 Singleton instance.
static void unlink ()
 Remove the semaphore name from the namespace (POSIX only).
static void unlink ()=delete
 Disabled if HighPriv == false. Non-privileged mutexes cannot call unlink().

Detailed Description

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

Cross-platform named process-wide mutex primitive.

Overview

jh::sync::ipc::process_mutex is a low-level inter-process synchronization primitive that mirrors the behavior of std::timed_mutex, extended to operate across process boundaries via OS-level named semaphores. Each unique template literal S defines a globally visible named mutex.

Semantic model

  • Acts as an IPC primitive โ€” not a composition of higher-level constructs.
  • Provides the same contract as std::timed_mutex:
    • Exclusive ownership semantics (single holder at a time).
    • Non-recursive โ€” attempting to lock twice without unlocking causes deadlock.
    • Unlocking without ownership or multiple consecutive unlocks is undefined behavior.
  • Integrates with RAII-style locking utilities such as std::lock_guard, std::unique_lock, or equivalent user-defined wrappers.
  • Supports try_lock(), try_lock_for(), and try_lock_until() for timed acquisition.

Cross-platform behavior

  • POSIX: implemented via sem_open(), sem_wait(), and sem_post().
  • Windows / MSYS2: implemented via CreateSemaphore() and WaitForSingleObject().
  • All standard privilege levels are sufficient; administrative rights are not required.

Error and UB conditions

  • Calling unlock() without owning the mutex, or calling it multiple times, is undefined behavior.
  • Calling lock() twice without an intervening unlock() causes deadlock.
  • POSIX may silently ignore double-unlock; Windows may terminate the process.

Usage recommendation

Use RAII-style management whenever possible:

std::lock_guard guard(m); // automatically unlocks on scope exit
Cross-platform named process-wide mutex primitive.
Definition process_mutex.h:221
static process_mutex & instance()
Singleton instance.
Definition process_mutex.h:236

This ensures exception safety and correct pairing of lock()/unlock().

Template Parameters
SBare name string (letters, digits, dot, dash, underscore).
HighPrivIf true, exposes unlink() for POSIX.

Member Function Documentation

◆ lock()

template<jh::meta::TStr S, bool HighPriv = false>
void jh::sync::ipc::process_mutex< S, HighPriv >::lock ( )
inline

Acquire the lock (blocking).

Semantics

Blocks the calling thread or process until the mutex becomes available. Once acquired, the caller obtains exclusive ownership of the process-wide synchronization primitive.

Reentrancy

This mutex is non-recursive. Calling lock() twice from the same thread or process without a corresponding unlock() causes a self-deadlock, identical to the behavior of std::timed_mutex.

Contract

  • Each lock() call must eventually be paired with unlock().
  • Re-locking a held mutex results in deadlock.
  • Unlocking without ownership is undefined behavior.

Usage recommendation

When using process_mutex within a lexical scope, prefer RAII-style management with std::lock_guard or an equivalent wrapper to ensure exception-safe unlock.

Exceptions
std::runtime_errorif the underlying system call fails unexpectedly.

◆ try_lock_for()

template<jh::meta::TStr S, bool HighPriv = false>
template<typename Rep, typename Period>
bool jh::sync::ipc::process_mutex< S, HighPriv >::try_lock_for ( const std::chrono::duration< Rep, Period > & d)
inline

Attempt to acquire the lock, waiting for a maximum duration.

On Windows, this maps to WaitForSingleObject with a bounded timeout. On POSIX systems with the Realtime Extension (POSIX.1b) (e.g. Linux/glibc), this maps to sem_timedwait. On pure POSIX systems without the extension (e.g. Darwin, BSD), timed waiting is emulated with sem_trywait + exponential backoff sleep, which approximates the same semantics while avoiding busy spinning.

Template Parameters
RepRepresentation type of duration.
PeriodPeriod type of duration.
Parameters
dDuration to wait.
Returns
true if lock acquired, false if timed out.
Exceptions
std::runtime_errorif the underlying system call fails unexpectedly.
Note
See try_lock_until for details.

◆ try_lock_until()

template<jh::meta::TStr S, bool HighPriv = false>
template<typename Clock, typename Duration>
bool jh::sync::ipc::process_mutex< S, HighPriv >::try_lock_until ( const std::chrono::time_point< Clock, Duration > & tp)
inline

Attempt to acquire the lock until an absolute time point.

Windows uses WaitForSingleObject with a computed relative timeout. POSIX with the Realtime Extension (POSIX.1b) (e.g. Linux/glibc) uses sem_timedwait with an absolute timespec. Pure POSIX systems without the extension (e.g. Darwin, BSD) emulate timed waiting via sem_trywait + exponential backoff sleep, preserving observable semantics (success/timeout) without excessive CPU usage.

Template Parameters
ClockClock type.
DurationDuration type.
Parameters
tpAbsolute time point at which the attempt should time out.
Returns
true if lock acquired, false if timed out.
Exceptions
std::runtime_errorif the underlying system call fails unexpectedly.
Note
Backoff is doubled each iteration, capped at 5 ms, to balance responsiveness and CPU usage.

◆ unlink()

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

Remove the semaphore name from the namespace (POSIX only).

Semantics

  • On POSIX systems, this calls sem_unlink() with the internally constructed name.
  • If the semaphore exists and is successfully unlinked:
    • The name is removed immediately from the namespace.
    • Existing open handles (in this or other processes) remain valid until they are closed via sem_close().
    • The semaphore object is destroyed only when the last handle closes.
  • If the semaphore name does not exist (errno == ENOENT): the call is silently ignored. No exception is thrown.
  • If sem_unlink() fails for any other reason (e.g. permissions, resource errors), an exception is thrown.

Idempotency

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

Windows

On Windows / MSYS2, there is no unlink concept. Named semaphores are automatically destroyed by the OS when the last handle is closed.

◆ unlock()

template<jh::meta::TStr S, bool HighPriv = false>
void jh::sync::ipc::process_mutex< S, HighPriv >::unlock ( )
inline

Release the lock.

Semantics

Releases ownership of the process-wide mutex. This operation must only be called by a participant that currently holds the lock; calling unlock() without prior ownership or invoking it multiple times consecutively constitutes undefined behavior.

Error model

  • On POSIX systems, improper unlocking may silently fail or leave the semaphore count inconsistent.
  • On Windows, calling ReleaseSemaphore() from a thread that does not own the lock may raise a runtime error or terminate the process.

Contract

  • The caller is responsible for ensuring lock ownership before calling unlock().
  • Repeated unlocking or unlocking without acquisition is UB.
  • If required, ownership can be tracked manually (e.g. via thread_local flags or process-scoped state) depending on usage scope.
Exceptions
std::runtime_errorif the underlying system call fails unexpectedly.

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