Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Stroika::Foundation::Execution::CallbackRegistry< FUNCTION_SIGNATURE > Class Template Reference

The callbacks an object calls when something happens - each added, and removed by the ID Add gave it - with the guarantees that needs when they are called on a thread of the object's own, and added and removed on others. More...

Detailed Description

template<typename FUNCTION_SIGNATURE>
class Stroika::Foundation::Execution::CallbackRegistry< FUNCTION_SIGNATURE >

The callbacks an object calls when something happens - each added, and removed by the ID Add gave it - with the guarantees that needs when they are called on a thread of the object's own, and added and removed on others.

o Call holds no lock while a callback runs. So Add never waits for a running callback, and a callback may Add, Remove (itself too) or Call, or wait for a thread doing so. o Once Remove returns, that callback is not running, and is never called again - except that, called from within that very callback, the call already under way finishes. So Remove waits for a call running on another thread: do not call it holding a lock that callback takes. o An exception from a callback is logged (DbgTrace) and ignored, and the rest are still called - except Thread::AbortException, which ends the Call, so its thread can end. o 'Before' and 'after' are as the registry's lock orders them - each Add, Remove and RemoveAll, and the start of each Call, takes it in turn (from different threads at once, in no defined order). So a Call calls the callbacks added before it started, and not removed since: one added during a Call is first called by the next.

Its lock is a plain (non-recursive) mutex, held only to read or update the list of callbacks - never while one runs, so nothing can re-enter it - and so cheap: one uncontended lock to Add or Remove; to Call, one, and two more for each callback it runs. (A use that cannot afford even that need not use this.)

Note
Defined only for callbacks returning void: Call runs any number of them, so has no one result to give back - and choosing one (the first's, the last's, or all of them, as boost::signals2's 'combiners' do) is a different abstraction.
It replaces Execution::Function's job of making callbacks removable - a footgun. A Function equals only copies of itself (each construction is given a new number), so removing a callback works only with the Function added, or a copy of it - and nothing enforces that, as a lambda converts to a Function implicitly:
monitor.AddCallback ([this] (const Event& e) { OnEvent_ (e); });
...
monitor.RemoveCallback ([this] (const Event& e) { OnEvent_ (e); }); // a new Function: removes nothing
That compiles, runs, and silently leaves the callback in place - to be called after its 'this' is gone. And the equality is that wrapper's own, around a copyable function<>, where the standard's wrappers - function<>, C++23's move_only_function, C++26's copyable_function - compare only to nullptr, by design. Here the registration, not the callable, has the identity: Add returns an ID, the only thing Remove takes, so there is nothing to get wrong; and nothing is asked of a callback but to be called (so, once C++23 is the floor, it could be a move_only_function).
Thread-Safety Internally-Synchronized-Thread-Safety
Example Usage
CallbackRegistry<void (const Event&)> fOnEvent; // in an object that calls them from a thread of its own
...
auto id = fOnEvent.Add ([] (const Event& e) { DbgTrace ("{}"_f, e); });
fOnEvent.Call (e); // on that thread: each callback, in the order added
fOnEvent.Remove (id); // once this returns, that callback is not running, and is not called again
#define DbgTrace
Definition Trace.h:327
The callbacks an object calls when something happens - each added, and removed by the ID Add gave it ...

Definition at line 83 of file CallbackRegistry.h.


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