C++ Logo


Advanced search

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

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Wed, 13 Sep 2023 17:07:27 +0100
On Wed, 13 Sept 2023 at 16:49, Nikl Kelbon <kelbonage_at_[hidden]> wrote:

> Where it is undefined? There are no such precondition, I quoted it

The precondition is implied by the effects. It says the mutexes are locked
by a sequence of calls to lock, try_lock or unlock. After one of them is
locked, calling lock on the other argument violates the precondition for
lock, and calling try_lock on it violates the precondition for try_lock.

If std::lock calls lock and try_lock, then the preconditions of those
functions are relevant.

Admittedly, a hostile reading of the current wording would allow locking
the second mutex by calling unlock (even though that's impossible).

We could add something to make the implied precondition explicit, but
phrasing it is tricky because not all mutex types have the precondition on
try_lock (e.g. std::recursive_mutex does not). It basically wants to say
that at every step in the algorithm, if calling lock() or try_lock() on one
of the not-yet-locked arguments would be undefined, the behaviour is
undefined. Which seems like a tautology to me.

> ср, 13 сент. 2023 г., 17:43 Jonathan Wakely <cxx_at_[hidden]>:
>> On Wed, 13 Sept 2023 at 16:20, Nikl Kelbon via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>> Postcondition of std::lock and std::try_lock:
>>> All of Mutexes... are locked, no deadlock occurs.
>>> *Effects*: All arguments are locked via a sequence of calls to lock(),
>>> try_lock(), or unlock() on each argument.
>>> <https://eel.is/c++draft/thread.lock.algorithm#5.sentence-1>
>>> The sequence of calls does not result in deadlock, but is otherwise
>>> unspecified.
>>> <https://eel.is/c++draft/thread.lock.algorithm#5.sentence-2>
>>> But in an common case, when there is a type with a mutex and you need to
>>> swap it
>>> void swap(Type& other) {
>>> std::scoped_lock l(this->mutex, other.mutex);
>>> // ... do swap ...
>>> }
>>> If addressof(other) == this same mutex will be locked, and there are no
>>> precondition which blocks this usage.
>>> But in fact its undefined behavior (implementation uses try_lock on
>>> already locked mutex) (deadlock on msvc-stl, just ub on others)
>>> There are several solutions to this, either we explicitly add
>>> precondition, or the implementation must handle this (then there could
>>> potentially be an overhead when unlocking)
>> Or we do nothing. The precondition already exists, whether we add it
>> explicitly or not.
>> This case is the same as std::lock(m, m) which is undefined already.
>> Being more explicit might not hurt though.

Received on 2023-09-13 16:07:42