callonce  1.0.0
Header-only memoized call-once wrapper for any C++ callable
fedem::utility::lock_free Class Reference

Thread-safe, lock-free execution policy. More...

#include <memoized_invoke.hh>

+ Collaboration diagram for fedem::utility::lock_free:

Public Member Functions

 lock_free ()=default
 Construct in the not-started state. More...
 
 lock_free (lock_free const &) noexcept
 Copy-construct: the new object starts not-started regardless of other. More...
 
 lock_free (lock_free &&) noexcept
 Move-construct: the new object starts not-started regardless of other. More...
 
lock_freeoperator= (lock_free const &) noexcept
 Copy-assign: resets *this to not-started (the source state is not copied). More...
 
lock_freeoperator= (lock_free &&) noexcept
 Move-assign: resets *this to not-started (the source state is not moved). More...
 
bool try_enter ()
 Atomically claim the right to run the callable. More...
 
void mark_done () noexcept
 Publish the done state and wake every waiter. More...
 
void mark_free () noexcept
 Publish the not-started state (reset / exception rollback) and wake waiters. More...
 
bool is_done () const noexcept
 
void wait () const noexcept
 Block until the state is no longer running. More...
 

Private Attributes

std::atomic< detail::execution_statestate_ { detail::execution_state::not_started }
 

Detailed Description

Thread-safe, lock-free execution policy.

lock_free implements the ExecutionPolicy interface expected by memoized_invoke on top of a std::atomic<execution_state>:

  • try_enter() is a single compare_exchange_strong from not_started to running. Exactly one thread wins the race and runs the callable.
  • Every other thread calls wait(), which parks on std::atomic::wait (C++20) until the winner publishes a new state.
  • mark_done() / mark_free() store the new state with memory_order_release and notify_all() the waiters, so the cached result is visible to every thread that observes done.

Copy and move re-initialise the state to not_startedstd::atomic is neither copyable nor movable, and a copy has its own synchronisation context, so inheriting a "done" flag from the source would be unsound.

Note
reset() and the argument-changing operator() on the owning memoized_invoke are still not thread-safe: they mutate the stored arguments and cache, which are ordinary members. Only the first-run race is synchronised.
See also
single_threaded

Constructor & Destructor Documentation

◆ lock_free() [1/3]

fedem::utility::lock_free::lock_free ( )
default

Construct in the not-started state.

◆ lock_free() [2/3]

fedem::utility::lock_free::lock_free ( lock_free const &  )
inlinenoexcept

Copy-construct: the new object starts not-started regardless of other.

◆ lock_free() [3/3]

fedem::utility::lock_free::lock_free ( lock_free &&  )
inlinenoexcept

Move-construct: the new object starts not-started regardless of other.

Member Function Documentation

◆ operator=() [1/2]

lock_free& fedem::utility::lock_free::operator= ( lock_free const &  )
inlinenoexcept

Copy-assign: resets *this to not-started (the source state is not copied).

References fedem::utility::detail::not_started, and state_.

◆ operator=() [2/2]

lock_free& fedem::utility::lock_free::operator= ( lock_free &&  )
inlinenoexcept

Move-assign: resets *this to not-started (the source state is not moved).

References fedem::utility::detail::not_started, and state_.

◆ try_enter()

bool fedem::utility::lock_free::try_enter ( )
inline

Atomically claim the right to run the callable.

Returns
true for the single thread that wins the CAS from not_started to running; false for every other caller (which should then call wait).

References fedem::utility::detail::not_started, fedem::utility::detail::running, and state_.

◆ mark_done()

void fedem::utility::lock_free::mark_done ( )
inlinenoexcept

Publish the done state and wake every waiter.

References fedem::utility::detail::done, and state_.

◆ mark_free()

void fedem::utility::lock_free::mark_free ( )
inlinenoexcept

Publish the not-started state (reset / exception rollback) and wake waiters.

References fedem::utility::detail::not_started, and state_.

◆ is_done()

bool fedem::utility::lock_free::is_done ( ) const
inlinenoexcept
Returns
true once a thread has completed the callable and called mark_done.

References fedem::utility::detail::done, and state_.

◆ wait()

void fedem::utility::lock_free::wait ( ) const
inlinenoexcept

Block until the state is no longer running.

Returns as soon as the state becomes done (the winner committed) or not_started (the winner's callable threw and rolled the state back). The caller must re-inspect the state after this returns.

References fedem::utility::detail::running, and state_.

Member Data Documentation

◆ state_

std::atomic< detail::execution_state > fedem::utility::lock_free::state_ { detail::execution_state::not_started }
private

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