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::conc::occ_box< T > Class Template Referencefinal

Generic container providing Optimistic Concurrency Control (OCC). More...

#include <jh/concurrent/occ_box.h>

Public Types

using value_type = T

Public Member Functions

 occ_box (const occ_box &other) noexcept
 Copy constructor: manually resets flag_ to false, cannot use =default.
occ_boxoperator= (const occ_box &other) noexcept
 Copy assignment: manually resets flag_ to false, cannot use =default.
 occ_box (occ_box &&other) noexcept
 Move constructor: transfers state but resets flag_ to false, cannot use =default.
occ_boxoperator= (occ_box &&other) noexcept
 Move assignment: transfers state but resets flag_ to false, cannot use =default.
 occ_box (std::shared_ptr< T > ptr)
 Construct a new occ_box from an existing shared_ptr<T>.
template<typename... Args>
 occ_box (Args &&... args)
 Construct a new occ_box by forwarding arguments to T.
template<typename F, typename... Args>
auto read (F &&f, Args &&... args) const
 Blocking read with optimistic validation.
template<typename F, typename... Args>
auto try_read (F &&f, std::uint16_t retries=1, Args &&... args) const -> std::optional< std::invoke_result_t< F, const T &, Args... > >
 Non-blocking read with limited retries.
template<typename F, typename... Args>
void write (F &&f, Args &&... args)
 Blocking write with optimistic commit-replace semantics.
template<typename F, typename... Args>
bool try_write (F &&f, std::uint16_t retries=1, Args &&... args)
 Non-blocking write with limited retries.
template<typename F, typename... Args>
void write_ptr (F &&f, Args &&... args)
 Blocking write using pointer replacement.
template<typename F, typename... Args>
bool try_write_ptr (F &&f, std::uint16_t retries=1, Args &&... args)
 Non-blocking pointer-based write with limited retries.
std::uint64_t get_version () const noexcept
 Get the current version counter of the box.

Detailed Description

template<typename T>
requires (std::is_copy_constructible_v<T> && std::is_move_constructible_v<T>)
class jh::conc::occ_box< T >

Generic container providing Optimistic Concurrency Control (OCC).

Template Parameters
TValue type, must be copy- and move-constructible.

Semantics

  • Encapsulates a value of type T with atomic versioned state.
  • Reads are wait-free: they either succeed with a consistent snapshot or retry internally.
  • Writes are commit-replace: each update creates a fresh state and replaces atomically via CAS.
  • No reader ever observes a partially written value.

Retry model

  • All try_*() APIs attempt once outside the loop, then retry up to N-1 times.
  • retries == 0 is equivalent to one attempt.
  • Backoff and jitter strategies can be layered on top (see examples).
Note
When JH_OCC_ENABLE_MULTI_COMMIT == 1 (default), boxes under apply_to() are given priority over single writes and reads, ensuring that multi-box transactions cannot be broken by concurrent commits.

Constructor & Destructor Documentation

◆ occ_box() [1/2]

template<typename T>
jh::conc::occ_box< T >::occ_box ( std::shared_ptr< T > ptr)
inlineexplicit

Construct a new occ_box from an existing shared_ptr<T>.

Parameters
ptrShared pointer to an already-constructed T.

Takes ownership by wrapping it into the initial state with version = 0.

◆ occ_box() [2/2]

template<typename T>
template<typename... Args>
jh::conc::occ_box< T >::occ_box ( Args &&... args)
inlineexplicit

Construct a new occ_box by forwarding arguments to T.

Template Parameters
ArgsArgument types for T's constructor.
Parameters
argsArguments perfectly forwarded to T's constructor.

Initializes the internal state with version = 0 and a freshly constructed std::shared_ptr<T>.

Member Function Documentation

◆ get_version()

template<typename T>
std::uint64_t jh::conc::occ_box< T >::get_version ( ) const
inlinenodiscardnoexcept

Get the current version counter of the box.

Returns
The version number associated with the current state.

Semantics

  • Each successful commit increments the version counter.
  • The counter is of type std::uint64_t and may wrap around on overflow.
  • To detect change, compare versions with != rather than ordering (>/<).
  • The version counter advances exactly once per successful commit, with no data races or partial state exposure.

◆ read()

template<typename T>
template<typename F, typename... Args>
auto jh::conc::occ_box< T >::read ( F && f,
Args &&... args ) const
inlinenodiscard

Blocking read with optimistic validation.

Template Parameters
FCallable type, must accept const T& and optional extra args.
ArgsAdditional argument types.
Parameters
fUser-provided callable, invoked with the current value.
argsOptional extra arguments forwarded to the callable.
Returns
R, the value returned by the callable, provided the read passes optimistic validation (snapshot is consistent).

Semantics

  • Performs a load-invoke-validate sequence under optimistic concurrency.
  • If the state changes between two atomic loads, the read retries internally.
  • Wait-free for readers: never blocks writers.

Why void is disallowed

Returning void is forbidden because read() must conceptually produce a value from the snapshot. Allowing void would encourage using this API solely for side effects, which violates the read model.

Permitted side effects

Minor auxiliary effects (e.g. updating a duration& for backoff logic, or writing to a log) are acceptable, provided they do not alter application state or depend on non-idempotent behavior.

For output purposes, prefer returning a value (e.g. a std::string built from an ostringstream) instead of directly printing inside read().

◆ try_read()

template<typename T>
template<typename F, typename... Args>
auto jh::conc::occ_box< T >::try_read ( F && f,
std::uint16_t retries = 1,
Args &&... args ) const -> std::optional< std::invoke_result_t< F, const T &, Args... > >
inlinenodiscard

Non-blocking read with limited retries.

Template Parameters
FCallable type, must accept const T& and optional extra args.
ArgsAdditional argument types.
Parameters
fUser-provided callable, invoked with the current value.
retriesMaximum number of attempts (0 treated as 1).
argsOptional extra arguments forwarded to the callable.
Returns
std::optional<R>, where R is the callable's return type. Returns std::nullopt if all retries fail validation.

Semantics

  • Performs optimistic load-invoke-validate like read().
  • Unlike read(), it gives up after at most retries attempts.
  • Retry count 0 is normalized to one attempt.

Purity rule

Pure side-effect-only operations are disallowed: this method must conceptually produce a value from the snapshot. Minor auxiliary effects (e.g. backoff instrumentation, logging) are acceptable if they do not alter application state.

Note
retries must be explicitly specified if extra args... are provided, since args always follow it in the parameter list.
See also
read()

◆ try_write()

template<typename T>
template<typename F, typename... Args>
bool jh::conc::occ_box< T >::try_write ( F && f,
std::uint16_t retries = 1,
Args &&... args )
inlinenodiscard

Non-blocking write with limited retries.

Template Parameters
FCallable type, must accept T& and optional extra args.
ArgsAdditional argument types.
Parameters
fUser-provided callable, applied to a freshly copied value.
retriesMaximum number of attempts (0 treated as 1).
argsOptional extra arguments forwarded to the callable.
Returns
true if the update is committed successfully, false if all attempts fail due to contention.

Semantics

  • Each attempt loads the current state, copies the value, applies the user function, and tries to commit via CAS.
  • Fails if another writer replaces the state before CAS succeeds.
  • Unlike write(), this method does not spin indefinitely: it retries at most retries times.

Copy semantics

Each attempt deep-copies the underlying value. If deep copies are undesirable, consider try_write_ptr() to construct and install a new object directly.

Usage note

For retryable operations or performance-sensitive paths, prefer try_write() or try_write_ptr() over their blocking counterparts, since they allow graceful failure handling under contention.

Note
retries must be explicitly specified if extra args... are provided, since args always follow it in the parameter list.
See also
write()

◆ try_write_ptr()

template<typename T>
template<typename F, typename... Args>
bool jh::conc::occ_box< T >::try_write_ptr ( F && f,
std::uint16_t retries = 1,
Args &&... args )
inlinenodiscard

Non-blocking pointer-based write with limited retries.

Template Parameters
FCallable type, must accept const std::shared_ptr<T>& and optional extra args, and return a new std::shared_ptr<T>.
ArgsAdditional argument types.
Parameters
fUser-provided callable, invoked with the old shared_ptr. Must return a new shared_ptr holding the replacement object.
retriesMaximum number of attempts (0 treated as 1).
argsOptional extra arguments forwarded to the callable.
Returns
true if the update is committed successfully, false if all attempts fail due to contention.

Semantics

  • Similar to try_write(), but avoids copying the old object.
  • Each attempt calls the user function to produce a fresh object and tries to commit it with CAS.
  • Stops after at most retries attempts.

Recommended scenarios

  • When replacing large objects where copying is wasteful.
  • When constructing a new object directly is cheaper than mutating a copy (e.g. resizing a string or vector before applying logic).
Note
retries must be explicitly specified if extra args... are provided, since args always follow it in the parameter list.
See also
try_write(), write_ptr()

◆ write()

template<typename T>
template<typename F, typename... Args>
void jh::conc::occ_box< T >::write ( F && f,
Args &&... args )
inline

Blocking write with optimistic commit-replace semantics.

Template Parameters
FCallable type, must accept T& and optional extra args.
ArgsAdditional argument types.
Parameters
fUser-provided callable, applied to a fresh copy of the object.
argsOptional extra arguments forwarded to the callable.
Returns
void

Semantics

  • Performs a load-copy-invoke-CAS loop until commit succeeds.
  • Always copies the current object before applying f.
  • Guarantees atomic replacement: readers never see a partially written object.

Performance notes

  • Safe under high contention: even if writes are very frequent, each commit is strictly atomic and never exposes torn or inconsistent states.
  • Excessive use may hurt performance due to repeated deep copies and CAS retries, but correctness and race-freedom are guaranteed.
  • Prefer embedding repeated logic inside f rather than invoking write() repeatedly in a loop.
  • If deep copies are undesirable, consider write_ptr() to construct and install a new object directly.

Return semantics

This method always returns void. The write is guaranteed to complete before returning. It is best suited for critical update paths where completion is mandatory.

Fairness

On most POSIX platforms, the scheduler tends to grant forward progress, so livelock is practically avoided. However, applications should not over-rely on this property.

See also
write_ptr()

◆ write_ptr()

template<typename T>
template<typename F, typename... Args>
void jh::conc::occ_box< T >::write_ptr ( F && f,
Args &&... args )
inline

Blocking write using pointer replacement.

Template Parameters
FCallable type, must accept const std::shared_ptr<T>& and optional extra args, and return a new std::shared_ptr<T>.
ArgsAdditional argument types.
Parameters
fUser-provided callable, invoked with the old shared_ptr. Must return a new shared_ptr holding the replacement object.
argsOptional extra arguments forwarded to the callable.
Returns
void

Semantics

  • Mechanism is the same as write(), but avoids deep copies.
  • Each attempt invokes the user function to construct a brand-new object and commits it atomically via CAS.
  • Retries indefinitely until success.

Recommended scenarios

  • When the object contains fields that can be safely discarded (e.g. large buffers or caches you don't need to preserve).
  • When the object has resizable members (like std::vector or std::string), and constructing directly at the new size is cheaper than copying and then resizing.
See also
write(), try_write_ptr()

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