https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89417

--- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Federico Kircheis from comment #3)
> My guess, without having looked at the implementation of std::lock, is that
> the algorithm uses try_lock to eventually lock/unlock the mutexes and see if
> it manages to lock both, in order to avoid the deadlock.

Right. So the potential deadlock helgrind complains about can't happen.


> But even if std::lock would be correct (and it is a false positive of
> valgrind), wouldn't sorting by address be an optimization?

Not in the uncontended case, no, because it does extra work to sort them. Your
make_lock function doesn't work for more than two arguments so a more complex
algorithm is needed for the general case (it doesn't even work for fewer than
two arguments, although obviously that's trivial to support).

> As far as I understand, if the mutexes are always locked in the same order,
> one does not need to try_lock.

That's not true. As I already said above, another thread could be locking the
same mutexes without using std::lock (in any order), or it could be locking a
different but overlapping set of mutexes.


> I'm therefore not saying the current algorithm should be dismissed, just
> asking if ordering before applying the algorithm could have any benefit.
> Apart from the fact that helgrind would not complain anymore, which IMHO
> would already be a big win.

Changing the locking algorithm to avoid a false positive from a particular tool
seems unwise.

> Maybe there are known drawbacks to sorting by address, but as mutexes cannot
> be moved,

std::mutex cannot be moved, but std::lock and std::scoped_lock don't only work
with standard mutexes. You have no idea whether foo::Mutex or bar::Lockable can
be moved. You can't even assume that the address is meaningful, I could write a
copyable mutex where different copies share a lock:

class CopyableMutex
{
  std::shared_ptr<std::mutex> m = std::make_shared<std::mutex>();
public:
  CopyableMutex() = default;
  CopyableMutex(const CopyableMutex&) = default;

  void lock() { m->lock(); }
  bool try_lock() { return m->try_lock(); }
  void unlock() { m->unlock(); }
};

Reply via email to