C++ Logo

std-proposals

Advanced search

[std-proposals] Fwd: Lack of preconditions on std::lock/std::try_lock

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Thu, 14 Sep 2023 09:48:07 -0400
On Thu, Sep 14, 2023 at 1:55 AM Nikl Kelbon <kelbonage_at_[hidden]> wrote:
>
> > No, it does not. It is UB, but it shouldn't be in `scoped_lock`'s preconditions.
> Im about std::lock / std::try_lock
>
> > `std::lock` will lock each given mutex in
> turn, in an unspecified order.
>
> As quoted:
> > Effects: All arguments are locked via a sequence of calls to lock(), try_lock(), or unlock() on each argument.
>
> Sequence may be empty and do not contain any call to lock or try lock

... how do you get that from the sentence you quoted? In general a
"sequence" can indeed be empty, but not when it specifically says the
"sequence of calls" is "on each argument".

There is no language here that allows a valid implementation where
`std::lock` does not call `lock/try_lock` on one of its parameters
(unless a prior one in the sequence threw an exception).

> > Also, any user-provided swapping behavior *must* take into account
> self-swapping.
>
> No, in fact swap in any cases used to not handle self assign in move assign and this is rare case when it will break something

It's not that rare for naive swap behavior to break something in
self-swapping operations. Particularly for non-moveable members like
mutexes.

> > A precondition wouldn't fix it.
>
> But handling such case will. Precondition atleast will make it explicit, now without seeing implementation it is not obvious

It is not obvious? It's not obvious that passing the same mutex to a
function whose entire purpose is to lock every mutex it is given will
cause that mutex to be locked twice?

You have an unusual definition of "obvious".

> If you think it’s must not be handled by std::lock, then try write code that will handle this correctly for a pack of mutexes or at least just a few mutexes
>
> This is incredibly error prone

There is no "correct" version of this code where you can pass the same
non-recursive mutex to `lock` and get valid behavior. And since the
behavior of `std::lock` explicitly says that it will lock every mutex
it is given, this clearly will cause problems if you pass the same
non-recursive mutex to it.

As with any use of a non-recursive mutex, it is up to the user to make
sure that it doesn't get locked twice on the same thread.

Received on 2023-09-14 13:48:18