Search code examples
c++arraysconstructorraii

Locking an array of std::mutex using a std::lock_guard array


I have the following array of mutexes:

std::mutex mtx[5];

And I would like to lock them all with an RAII style:

std::lock_guard<std::mutex> grd[5] { mtx[0], mtx[1], mtx[2], mtx[3], mtx[4] };

While the above code works, it isn't ideal as I can't write it independently of the size of the array (here 5).

Is there a way to do that? Shall I work with template magic to create an std::initializer_list out of an array? (is that possible?)

I'm open to using std::array or std::vector instead of C-style arrays, I used those here for conciseness. Ideally this works in C++14 but any solution up to latest standards is fine.


Solution

  • What you want is std::scoped_lock. It takes N mutexes and locks them on creation and unlocks on destruction. That would give you

    std::scoped_lock sl{mtx[0], mtx[1], mtx[2], mtx[3], mtx[4]};
    

    If that's still too verbose you can wrap that in a factory function like

    // function that actually creates the lock
    template<typename Mutexes, std::size_t N, std::size_t... Is>
    auto make_scoped_lock(Mutexes (&mutexes)[N], std::index_sequence<Is...>)
    {
        return std::scoped_lock{mutexes[Is]...};
    }
    
    // helper function so you don't have to create your own index_sequence
    template<typename Mutexes, std::size_t N>
    auto make_scoped_lock(Mutexes (&mutexes)[N])
    {
        return make_scoped_lock(mutexes, std::make_index_sequence<N>{});
    }
    
    int main()
    {
        std::mutex mtx[5];
        auto lock = make_scoped_lock(mtx);
    }
    

    If you switch to using a std::array to hold the mutexes then the code can be simplified to a call to std::apply to do the expansion of the array into a parameter pack like

    template<typename Mutexes>
    auto make_scoped_lock(Mutexes& mutexes)
    {
        return std::apply([](auto&... mutexes) { return std::scoped_lock{mutexes...}; }, 
                          mutexes);
    }
    
    int main()
    {
        std::array<std::mutex, 5> mtx;
        auto sl = make_scoped_lock(mtx);
    }