Flow 3.0.0
Flow project: Public API.
Public Types | Public Member Functions | List of all members
flow::util::Locked_proxy< Target_t, Mutex_t > Class Template Reference

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...
 

Detailed Description

template<typename Target_t, typename Mutex_t>
class flow::util::Locked_proxy< Target_t, Mutex_t >

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}:

const auto n = w_locked()->compute_n(); // Lock m; invoke w.compute_n(); save the result; unlock 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, ...>.

Keep it to one expression

*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.

See also
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.
Template Parameters
Target_tType of the object to which *this proxies access; possibly const-qualified (see above).
Mutex_tA Lockable-concept mutex type, e.g. util::Mutex_non_recursive.

Constructor & Destructor Documentation

◆ Locked_proxy() [1/2]

template<typename Target_t , typename Mutex_t >
flow::util::Locked_proxy< Target_t, Mutex_t >::Locked_proxy ( Target *  target,
Mutex *  mutex 
)
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.)

Parameters
targetThe object to which operator->() shall forward. Behavior undefined (assertion may trip) if null.
mutexThe 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.

◆ Locked_proxy() [2/2]

template<typename Target_t , typename Mutex_t >
flow::util::Locked_proxy< Target_t, Mutex_t >::Locked_proxy ( Locked_proxy< Target_t, Mutex_t > &&  src_moved)
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.

Parameters
src_movedSource object. Its destructor shall do nothing after this returns; it must not be dereferenced.

Member Function Documentation

◆ operator*()

template<typename Target_t , typename Mutex_t >
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).

Returns
See above.

◆ operator->()

template<typename Target_t , typename Mutex_t >
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).

Returns
See above.

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