Document Number: | N4033
Date: | 2014-05-23
Author: | Anthony Williams Just Software Solutions Ltd

# N4033: `synchronized_value<T>` for associating a mutex with a value

A couple of years ago I wrote an article for Dr Dobb's Journal discussing a `synchronized_value` template to associate a mutex with a value. I'd like to propose that template for standardization, with a few modifications.

The basic idea is that `synchronized_value<T>` stores a value of type `T` and a mutex. It then exposes a pointer interface, such that derefencing the pointer yields a special wrapper type that holds a lock on the mutex, and that can be implicitly converted to `T` for reading, and which forwards any values assigned to the assignment operator of the underlying `T` for writing. There is also an arrow operator which allows member functions on the wrapped value to be called. e.g.

    synchronized_value<std::string> s;

    std::string readValue()
    {
        return *s;
    }

    void setValue(std::string const& newVal)
    {
        *s=newVal;
    }

    void appendToValue(std::string const& extra)
    {
        s->append(extra);
    }

All three of these functions can be called by multiple threads concurrently, and the implicit mutex locks will ensure that the calls are serialized so there are no data races.

The proposed interface also provides a separate `update_guard<T>` template that locks the mutex in a `synchronized_value<T>` object for a longer period, thus providing the ability to perform multiple operations under a single lock. This can be important for things like message queues, where you want to check if the queue has a value before trying to pop from the queue.

    synchronized_value<std::queue<message_type>> queue;
    void process_message(){
        std::optional<message_type> local_message;
        {
            update_guard<std::queue<message_type>> guard(queue);
            if(!guard->empty()){
                local_message.emplace(guard->front());
                guard->pop_front();
            }
            else return;
        }
        do_processing(local_message.value());
    }

Finally, this proposal adds an `apply()` member function to the `synchronized_value<T>` template, which locks the mutex, and passes a reference to the stored value to the supplied function. The previous example could thus be written as follows:

    synchronized_value<std::queue<message_type>> queue;
    void process_message(){
        std::optional<message_type> local_message;
        queue.apply([&](std::queue<message_type>& q){
            if(!q.empty()){
                local_message.emplace(q.front());
                q.pop_front();
            }
        }
        if(local_message)
            do_processing(local_message.value());
    }

## Proposed Wording

Add a new section to chapter 30 as follows.

> ### 30.x Synchronized Values

> This section describes a class template to associate a mutex (30.4) with a value in order to facilitate the construction of race-free programs.

> #### Header <synchronized_value> synopsis

>     namespace std {
>         template<typename T>
>         class synchronized_value;

>         template<typename T>
>         class update_guard;
>     }

> #### 30.x.1 Class template `synchronized_value`

>     namespace std
>     {
>         template<typename T>
>         class synchronized_value
>         {
>         public:
>             synchronized_value(synchronized_value const&) = delete;
>             synchronized_value& operator=(synchronized_value const&) = delete;

>             template<typename ... Args>
>             synchronized_value(Args&& ... args);
>             ~synchronized_value();

>             template<typename F>
>             auto apply(F&& func) -> typename std::result_of<F(T&)>::type;

>             unspecified operator->();
>             unspecified operator*();
>         };
>     }

> An object of type `synchronized_value<T>` wraps an object of type `T` along with a mutex to ensure that only one thread can access the wrapped object at a time. The wrapped object can be accessed through the pointer dereference and member access operators, through an instance of the std::update_guard class template, or by passing a function or callable object to the `apply` member function. All such accesses are done with the internal mutex locked.

> #####

>     template<typename ... Args>
>     synchronized_value(Args&& ... args);

> Requires:
>   `T` is constructible from `args`
> Effects:
>   Constructs a `std::synchronized_value` instance containing an object constructed with `T(std::forward<Args>(args)...)`. If no arguments are supplied then the wrapped object is default-constructed.
> Throws:
>   Any exceptions thrown by the construction of the wrapped object.

> #####

>     ~synchronized_value();

> Effects:
>   Destroys `*this` and the contained object of type `T`.

> #####

>     template<typename F>
>     auto apply(F&& func) -> typename std::result_of<F(T&)>::type;

> Effects:
>   Locks the internal mutex, calls `func(t)`, where `t` is the wrapped object of type `T` store in `*this`, then unlocks the internal mutex.
> Returns:
>   The return value of the call to `func`.
> Throws:
>   `std::system_error` if the lock could not be acquired. Any exceptions thrown by the call to `func(t)`. Note: the internal mutex is unlocked after the call, even if `func(t)` exits with an exception.
> Synchronization:
>   Multiple threads may call `apply()`, `operator->()` or `operator*()` on the same instance of `synchronized_value` concurrently without external synchronization. If multiple threads call `apply()`, `operator->()` or `operator*()` concurrently on the same instance then the behaviour is as-if they each made their call in some unspecified order. The completion of the full expression associated with one access synchronizes-with a subsequent access to the wrapped object through `*this`.

> #####

>     unspecified operator->();

> Requires:
>   Given an object `sv` of type `std::synchronized_value<T>`, and an object `p` of type `T*`, `sv->some-expr` is valid if and only if `p->some-expr` would be valid.
> Effects:
>   Locks the internal mutex associated with `*this` and returns an object that implements the member access operator to access the wrapped `T` object. Unlocks the internal mutex at the end of the full expression.
> Note:
>   Multiple accesses to the same `synchronized_value` object within the same full expression will lead to deadlock.
> Throws:
>   `std::system_error` if the lock could not be acquired.
> Synchronization:
>   Multiple threads may call `apply()`, `operator->()` or `operator*()` on the same instance of `synchronized_value` concurrently without external synchronization. If multiple threads call `apply()`, `operator->()` or `operator*()` concurrently on the same instance then the behaviour is as-if they each made their call in some unspecified order. The completion of the full expression associated with one access synchronizes-with a subsequent access to the wrapped object through `*this`.

> #####

>     unspecified operator*();

> Effects:

>   Locks the internal mutex associated with `*this` and returns an object that provides access to the wrapped `T` object. Unlocks the internal mutex at the end of the full expression.

>   The expression `*sv=x` assigns the value `x` to the wrapped object, and requires that `T` is MoveAssignable from `x`.

>   `*sv` is implicitly convertible to `T`. Such a conversion copy-constructs a new `T` object from the stored value, and thus requires that `T` is CopyConstructible.

> Note:
>   Multiple accesses to the same `synchronized_value` object within the same full expression will lead to deadlock.
> Throws:
>   `std::system_error` if the lock could not be acquired.
> Synchronization:
>   Multiple threads may call `apply()`, `operator->()` or `operator*()` on the same instance of `synchronized_value` concurrently without external synchronization. If multiple threads call `apply()`, `operator->()` or `operator*()` concurrently on the same instance then the behaviour is as-if they each made their call in some unspecified order. The completion of the full expression associated with one access synchronizes-with a subsequent access to the wrapped object through `*this`.

> #### 30.x.2 `update_guard` Class Template

>     namespace std {
>         template <class T>
>         class update_guard
>         {
>         public:
>             explicit update_guard(synchronized_value<T>& sv);
>             ~update_guard();

>             T& operator*() noexcept;
>             T* operator->() noexcept;

>             update_guard(update_guard const& ) = delete;
>             update_guard& operator=(update_guard const& ) = delete;
>         };
>     }

> An instance of `update_guard` locks the internal mutex of the supplied `synchronized_value` object for the lifetime of the `update_guard` object. It provides a means of accessing the stored value multiple times without releasing and reacquiring the lock.

> #####

>     update_guard(synchronized_value& sv);

> Effects:
>   Constructs a new `update_guard` with `sv` as the associated `std::synchronized_value` instance. Locks the mutex for `sv` for the current thread.
> Throws:
>   `std::system_error` if the lock could not be acquired.

> #####

>     ~update_guard();

> Effects:
>   Destroys `*this` and unlocks the mutex for the associated `synchronized_value` object.

> #####

>     T* operator->();

> Returns:
>   A pointer to the object of type `T` stored in the associated `synchronized_value` object.

> #####

>     T& operator*();

> Returns:
>   A reference to the object of type `T` stored in the associated `synchronized_value` object.