views:

303

answers:

2

C++0x thread library or Boost.thread define non-member variadic template function that lock all lock avoiding dead lock.

template <class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);

While this function avoid help to deadlock, the standard do not includes the associated scoped lock to write exception safe code.

{
  std::lock(l1,l2);
  // do some thing
  // unlock li l2 exception safe
}

That means that we need to use other mechanism as try-catch block to make exception safe code or define our own scoped lock on multiple mutexes ourselves or even do that

{
  std::lock(l1,l2);
  std::unique_lock lk1(l1, std::adopted);
  std::unique_lock lk2(l2, std::adopted);
  // do some thing
  // unlock li l2 on destruction of lk1 lk2
}

Why the standard doesn't includes a scoped lock on multiple mutexes of the same type, as for example

{
  std::array_unique_lock<std::mutex> lk(l1,l2);
  // do some thing
  // unlock l1 l2 on destruction of lk
}

or tuples of mutexes

{
  std::tuple_unique_lock<std::mutex, std::recursive_mutex> lk(l1,l2);
  // do some thing
  // unlock l1 l2 on destruction of lk
}

Is there something wrong on the design?


Updated: description from the standard

template <class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);

Requires: Each template parameter type shall meet the Mutex requirements, except that a call to try_- lock() may throw an exception. [ Note: The unique_lock class template meets these requirements when suitably instantiated. —end note ]

Effects: All arguments are locked via a sequence of calls to lock(), try_lock(), or unlock() on each argument. The sequence of calls shall not result in deadlock, but is otherwise unspecified. [ Note: A deadlock avoidance algorithm such as try-and-back-off must be used, but the specific algorithm is not specified to avoid over-constraining implementations. —end note ] If a call to lock() or try_lock() throws an exception, unlock() shall be called for any argument that had been locked by a call to lock() or try_lock().


I have accept the answer. I understand that the main reason is because there is no enough time to make the C++0x Thread library better. I hope that TR2 will include much more things.

A: 

Constructing one object that locks multiple locks does not avoid any deadlocks any more than locking them individually. You can't lock two locks simultaneously.. unless you use two threads, which kind of defeats the point. This holds true even if you put them in the same statement.

DeadMG
Please see the standard. It uses an algorithm that uses try_lock and unlock that help to avoid deadlock,in cases two thread take both logs using the same function.
Vicente Botet Escriba
How is that different to the OP just locking them both by himself?Could be that I misread the question, but I thought that he was suggesting that by having such a multiple lock structure, you would obviously instantly avoid deadlocks as opposed to locking them one after another.
DeadMG
No, it was about exception safety and RAII style multi-locks. Read the Boost.Thread documentation or the C++0x draft about `lock()`.
Georg Fritzsche
@DeadMg please read the effect of lock(...) I had added on the question
Vicente Botet Escriba
+1  A: 

I think that by providing defer_lock_t (and adopt_lock_t) the expectation is that usage will be like your second example, or probably more like:

 std::unqiue_lock ul1(l1, std::deferred);
 std::unique_lock ul2(l2, std::deferred);
 std::lock(ul1, ul2);

This is exception safe and all that good stuff.

I certainly can't pretend to know the minds of the designers, but my guess is they are making an effort to provide a minimal set of portable, safe, primitives. A scoped multiple lock type is just so much icing, and it's icing that if in the standard needs to be specified and designed, or in boost.thread, icing that needs to be implemented (and of course ultimately the standard has to be concerned about implementation too, look what happened with export).

Logan Capaldo
`export` is a completely different category and (would have) required major architectural changes in the compilers. Why do you think it needs so much icing? It should be just write-once in terms of the primitives already given.
Georg Fritzsche
@Logan Sorry I make the error to use adopted instead of adopt_lock.
Vicente Botet Escriba
@gf, that's sort of my point. It _is_ write once in terms of the primitives already given, which is what makes it icing, which is why it doesn't need to be in the standard or a burden to the implementers.
Logan Capaldo
@Logan I understand all your explanations, at least for std, less for Boost that is intended to propose new tools to experiment with.
Vicente Botet Escriba
export, incidentally may be a bad example, but I just wanted to emphasize the idea that no matter how simple, easy or obvious it might be to implement something in principle, that doesn't make it free, the work still has to happen in practice.
Logan Capaldo
@Logan I don't see any difference between the code you propose and the one I presented. Either you lock all and adopt or you defer and the lock all. Is the same, isn't it?
Vicente Botet Escriba
@Logan Just for curiosity. If a variadic unique lock were on the standard, will you use it?
Vicente Botet Escriba
@Vicente I think setting up the lock guards first is slightly safer, because you don't accidentally add a lock to the call to lock and forget to add the adoption, since the guards come first. At least I feel it would be less likely to make a mistake with that ordering anyway. As for using a variadic lock template if it existed, if I needed to lock multiple locks like that I probably would. I try to avoid putting myself in that position though.
Logan Capaldo
I could agree, but a user can forget also to defer the lock while setting the lock guards. It is clear that a function will solve any issues.I have needed to lock several mutex at a time while implementing TBoost.STM a Software Transactional Memory library that hides all the synchronization machinery to the user, hence the need to lock multiple mutex some times. I'm unable to say if this is commonly needed.
Vicente Botet Escriba