|
Flow 3.0.0
Flow project: Public API.
|
A simple locking-proxy class template (execute-around-pointer idiom): at construction locks the given mutex and provides -> access to the given target object; at destruction unlocks it.
More...
#include <util.hpp>
Public Types | |
| using | Target = Target_t |
| Alias for template parameter. | |
| using | Mutex = Mutex_t |
| Alias for template parameter. | |
Public Member Functions | |
| Locked_proxy (Target *target, Mutex *mutex) | |
Locks *mutex – blocking as-needed until that is possible – and makes *this proxy *target. More... | |
| Locked_proxy (Locked_proxy &&src_moved) | |
Move constructor: *this acts as src_moved would-have (in particular its eventual destruction unlocks the mutex), while src_moved becomes a no-op object permanently. More... | |
| Locked_proxy (const Locked_proxy &)=delete | |
Prohibit copying: for each explicit ctor invocation, there shall be exactly 1 unlocking dtor invocation. | |
| Locked_proxy & | operator= (const Locked_proxy &)=delete |
Prohibit copying (see deleted copy ctor). | |
| Locked_proxy & | operator= (Locked_proxy &&)=delete |
Prohibit modifying existing *this; except that moving-from is enabled via the move ctor. | |
| Target * | operator-> () const |
Returns pointer to the target object, the guarding mutex being locked by *this (do not use if moved-from). More... | |
| Target & | operator* () const |
Returns reference to the target object, the guarding mutex being locked by *this (do not use if moved-from). More... | |
A simple locking-proxy class template (execute-around-pointer idiom): at construction locks the given mutex and provides -> access to the given target object; at destruction unlocks it.
In typical use one obtains *this as an unnamed temporary in the middle of an expression, so that the mutex is locked for exactly that expression's duration. E.g., given Widget w and a mutex m serializing all access to w – suppose some w_locked() returns Locked_proxy{&w, &m}:
This is the execute-around-pointer idiom. The typical motivation: some class C internally owns both a mutex-protected object and the mutex, its background/concurrent machinery locking the latter as needed; and wants to let its user safely invoke methods of that object. Then C can supply a w_locked()-style accessor as sketched above – instead of writing a delegating, internally-locking method for every operation of interest. The set of available operations then maintains itself, as does the correctness of each such call's locking.
To grant read-only (const methods only) access, simply use a const-qualified Target type: e.g., Locked_proxy<const Widget, ...>.
*this existing = the mutex being locked. The intended use is as an unnamed temporary (see above example): then the lock is held for a few microseconds, and it is impossible to leak. If you do save a *this to extend the locked section, keep the scope tight: whatever machinery normally locks that mutex is blocked in the meantime.
boost::synchronized_value implements the same idiom but owns both the payload and the mutex ("wrap your data in me"). *this, by contrast, is non-owning: it is for objects whose guarding mutex exists independently – e.g., one that guards more state than just the target object.| Target_t | Type of the object to which *this proxies access; possibly const-qualified (see above). |
| Mutex_t | A Lockable-concept mutex type, e.g. util::Mutex_non_recursive. |
|
explicit |
Locks *mutex – blocking as-needed until that is possible – and makes *this proxy *target.
The destructor invocation shall unlock it.
*this cannot be copied, but it can be moved. As a result, it is guaranteed that the aforementioned unlocking will occur exactly once; however it can occur via the destructor of another Locked_proxy that was move-constructed from *this, our own dtor therefore being a no-op. (A moved-from *this must not be dereferenced.)
| target | The object to which operator->() shall forward. Behavior undefined (assertion may trip) if null. |
| mutex | The mutex to hold for *this lifetime: the mutex – the one and only one – by which all access to *target is serialized (see class doc header for the typical setup). Must be non-null; must outlive *this. |
|
default |
Move constructor: *this acts as src_moved would-have (in particular its eventual destruction unlocks the mutex), while src_moved becomes a no-op object permanently.
| src_moved | Source object. Its destructor shall do nothing after this returns; it must not be dereferenced. |
| Target_t & flow::util::Locked_proxy< Target_t, Mutex_t >::operator* |
Returns reference to the target object, the guarding mutex being locked by *this (do not use if moved-from).
Useful, e.g., to print the target (os << *w_locked()) or to pass it to a function taking a reference – keeping in mind the mutex is locked only until the end of the full expression (in typical unnamed-temporary use).
| Target_t * flow::util::Locked_proxy< Target_t, Mutex_t >::operator-> |
Returns pointer to the target object, the guarding mutex being locked by *this (do not use if moved-from).