|
callonce
1.0.0
Header-only memoized call-once wrapper for any C++ callable
|
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_free & | operator= (lock_free const &) noexcept |
Copy-assign: resets *this to not-started (the source state is not copied). More... | |
| lock_free & | operator= (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_state > | state_ { detail::execution_state::not_started } |
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.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_started — std::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.
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.
|
default |
Construct in the not-started state.
|
inlinenoexcept |
Copy-construct: the new object starts not-started regardless of other.
|
inlinenoexcept |
Move-construct: the new object starts not-started regardless of other.
Copy-assign: resets *this to not-started (the source state is not copied).
References fedem::utility::detail::not_started, and state_.
Move-assign: resets *this to not-started (the source state is not moved).
References fedem::utility::detail::not_started, and state_.
|
inline |
Atomically claim the right to run the callable.
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_.
|
inlinenoexcept |
Publish the done state and wake every waiter.
References fedem::utility::detail::done, and state_.
|
inlinenoexcept |
Publish the not-started state (reset / exception rollback) and wake waiters.
References fedem::utility::detail::not_started, and state_.
|
inlinenoexcept |
true once a thread has completed the callable and called mark_done. References fedem::utility::detail::done, and state_.
|
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_.
|
private |
Referenced by is_done(), mark_done(), mark_free(), operator=(), try_enter(), and wait().