2

From a Container class, I'd like to lock a vector of boost::mutex, each one owned by a Controlled instance (weird code design, but for MWE purpose only).

// std::vector<Controlled*> _vData;

void Container::main_method()
{
  for (int i=0; i<_vData.size(); i++)
  {
    boost::mutex::scoped_lock my_lock(_vData.at(i)->_mutex);
    this->processing(i);
  }

  // precondition for post_processing(): all processing() calls done
  for (int i=0; i<_vData.size(); i++)
  {
    boost::mutex::scoped_lock my_lock(_vData.at(i)->_mutex);
    this->post_processing(i);
  }
}

But since processing is cpu-bound and Controlled objects are modified from elsewhere in the mean time, I'd like to simply do a cycled scoped_lock at the beginning of the main_method, in order to lock everything and asap, such as

void Container::desired_main_method()
{
  for (int i=0; i<_vData.size(); i++)
  {
    boost::mutex::scoped_lock my_lock(_vData.at(i)->_mutex);
  }

  // locks destroyed here, aren't they ?

  for (int i=0; i<_vData.size(); i++)
  {
    this->processing(i);
  }

  for (int i=0; i<_vData.size(); i++)
  {
    this->post_processing(i);
  }
}

Problem is, if I understanded well the RAII idiom and the scoped_lock context, that in this way, the locks would go out of scope soon after the lock for cycle ends.

I've tried to new an array of locks at Container ctor and to delete it at its dtor, but I guess this is against the RAII idiom itself.

What did I misunderstand, or how could I refactor the whole issue?

Patrizio Bertoni
  • 2,582
  • 31
  • 43

3 Answers3

2

Assuming that your question is: "how do I use a RAII-like scoped lock for multiple mutexes at the same time?"

Then you can either create your own RAII-wrapper for multiple locks or use something like a scope guard.

(Untested pseudocode, but hope you get the idea.)

template <typename TIterator>
class multiple_lock_guard
{
private:
    TIterator _begin, _end;

    multiple_lock_guard(TIterator begin, TIterator end)
        : _begin{begin}, _end{end}
    {
        for(auto it = _begin; it != _end; ++it)
        {
            it->_mutex.lock();
        }
    }

    ~multiple_lock_guard()
    {
        for(auto it = _begin; it != _end; ++it)
        {
            it->_mutex.unlock();
        }
    }
};

You can use it as follows:

void Container::desired_main_method()
{
    multiple_lock_guard mlg(std::begin(_vData), std::end(_vData));

    for(int i = 0; i < _vData.size(); i++)
    {
        this->processing(i);
    }

    for(int i = 0; i < _vData.size(); i++)
    {
        this->post_processing(i);
    }
}
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Why write your own? Standard library has a lot of features (surrounding `std::lock` with `defer_lock_t`). Boost makes it even more powerful with iterator-range interface (see my answer). Note it will be a lot more robust, because your implementation is naturally deadlock-prone – sehe Sep 13 '16 at 09:03
  • @sehe: Fair points - my answer aims to show the OP how to solve its "multiple lock guard" problem in a generic way that can be applied for similar issues and shows how to implement your own RAII wrapper. – Vittorio Romeo Sep 13 '16 at 09:44
1

How about the following?

void Container::desired_main_method()
{
  std::vector<boost::mutex::scoped_lock> locks;

  for (int i=0; i<_vData.size(); i++)
  {
    locks.emplace_back(_vData.at(i)->_mutex);
  }

  for (int i=0; i<_vData.size(); i++)
  {
    this->processing(i);
  }

  for (int i=0; i<_vData.size(); i++)
  {
    this->post_processing(i);
  }
}
Jeremy
  • 5,055
  • 1
  • 28
  • 44
1

You can already use the free function extensions from Boost Thread to atomically lock a collection of deferred locks:

Live On Coliru

#include <boost/thread.hpp>
#include <vector>

struct X {
    boost::mutex mutable mx;
};

void foo(std::vector<X> const& xs) {

    std::vector<boost::unique_lock<boost::mutex> > locks;
    for (auto& x : xs) {
        locks.emplace_back(x.mx, boost::defer_lock);
    }

    boost::lock(locks.begin(), locks.end());

    // all locks held
}

int main() {
    std::vector<X> xs(10);
    foo(xs);
}
sehe
  • 374,641
  • 47
  • 450
  • 633