Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to simulate Rust's Mutex<Object> in C++?

Tags:

c++

rust

Having an object and a mutex as 2 different variables is kinda error prone

MyObject myObject;
std::mutex myObjectMutex;

I tend to forget to lock it sometimes.

On Rust, a shared object is required to be inside a mutex:

std::sync::Mutex<MyObject> mySharedObject

So I have to use like this:

mySharedObject.lock().unwrap().objectMethod()

What would be the less error prone way to simulate something like this in C++ so I don't forget to lock it?

I thought of an std::tuple<std::mutex, MyObject> but it's not very good and I can forget to lock.

like image 885
Rafaelo Avatar asked Jun 15 '21 20:06

Rafaelo


1 Answers

One way to do it is have your Mutex<T> only allow access to the contained T via a lambda:

template <typename T>
class Mutex {
private:
    T value;
    std::mutex mutex;

public:
    // Fill out some constructors, probably some kind of emplacement constructor too.
    // For simplicity of the example, this should be okay:
    explicit Mutex(T value)
        : value(std::move(value))
    {}

    template <typename F>
    auto locked(F&& fn) const& -> std::invoke_result_t<F&&, T const&> {
        // Lock the mutex while invoking the function.
        // scoped_lock automatically unlocks at the end of the scope
        std::scoped_lock lock(mutex);
        return std::invoke(std::forward<F>(fn), value);
    }

    template <typename F>
    auto locked(F&& fn) & -> std::invoke_result_t<F&&, T&> {
        std::scoped_lock lock(mutex);
        return std::invoke(std::forward<F>(fn), value);
    }

    // Can be worth repeating for const&& and && as well
};

Usage:

mySharedObject.locked([] (MyObject& obj) { return obj.objectMethod(); });

It's still possible to defeat this by stashing a reference to the obj inside a .locked(...) call and using the reference outside of .locked(...), but that would almost require deliberately trying to do the wrong thing.

Also, be aware that it being in a lambda can be quite limiting, as regular control flow no longer works (return doesn't return from the outer scope, continue/break don't work, etc.)

like image 193
Justin Avatar answered Nov 13 '22 22:11

Justin