Date: Wed, 14 Jan 2026 21:47:17 +0000
On Wed, 14 Jan 2026 at 20:42, Ryan P. Nicholl <rnicholl_at_[hidden]>
wrote:
> Most types don't guarantee that you can destroy them while another thread
> is still using them, this isn't specific to std::latch. I think "it isn't
> possible to implement" is framing this completely wrong.
>
> It's also not true, you could implement it with a std::mutex and a
> std::condition_variable if you want to guarantee that waiters do not wake
> before a notify is complete.
>
>
> std::mutex and condition_variable can't be implemented using only the
> guarantees in the standard provided by std::atomic either.
>
They don't need to be, because they're provided by the standard library.
>
> I'm not sure how concerning this is in practice, given pthreads (which is
> usually used to implement mutex) says as follows:
>
> The *pthread_mutex_destroy*() function shall destroy the mutex object
> referenced by *mutex*; the mutex object becomes, in effect,
> uninitialized. An implementation may cause *pthread_mutex_destroy*() to
> set the object referenced by *mutex* to an invalid value. A destroyed
> mutex object can be reinitialized using *pthread_mutex_init*(); the
> results of otherwise referencing the object after it has been destroyed are
> undefined.
>
> It shall be safe to destroy an initialized mutex that is unlocked.
> Attempting to destroy a locked mutex results in undefined behavior.
>
> Seems the pthread mutex implementation guarantees this is safe, but the
> C++ standard, as best I can tell, does not provide a way to implement
> either behavior using std::atomic.
>
I don't read it that way. In the abstract machine there is no time between
the "unlock" and the conceptual "notify" that happens to inform other
threads that they can lock it. It's not like a std::atomic.
If the thread that unlocks the mutex also destroys it, then you know the
unlock is sequenced before the destroy. If those two operations happen in
separate threads, you need external synchronization to ensure that the
unlock happens before the destroy (otherwise you can't know that the
destroy doesn't happen while the mutex is still locked, which would be UB).
Either way, the return from pthread_mutex_unlock happens before the
pthread_mutex_destroy, so this isn't comparable to the case you're
describing where you want to destroy an atomic concurrently with a notify
on that atomic (which is just user error as Andrey said in another reply -
don't do that).
>
> Looking at MUSL, it seems like it's using a very similar strategy to
> libc++ for implementing pthread_mutex_unlock();
>
> LeaveCriticalSection has the same behavior.
>
> It looks like all major implementations use this type of "mutate and
> notify" operation internally, but the standard library doesn't expose any
> way to do this which is not relying on what seems like UB.
>
> If there is something somewhere which defines this as working, I'm
> interested to know what it would be.
>
> I do not think you can (legally) implement std::mutex using std::atomic in
> a way that doesn't rely on these behaviors.
>
The standard doesn't need to provide a way to implement std::mutex at all.
>
> Alternative: publish a DR that clarifies that "atomic_notify_*" functions
> are well defined and do not cause a race even if the object no longer
> exists at the pointed address. I think this will work for recent Windows
> STL, libc++, Glibc, and MUSL, at least for most types. I haven't checked
> other implementations.
>
>
I would not support such a change.
wrote:
> Most types don't guarantee that you can destroy them while another thread
> is still using them, this isn't specific to std::latch. I think "it isn't
> possible to implement" is framing this completely wrong.
>
> It's also not true, you could implement it with a std::mutex and a
> std::condition_variable if you want to guarantee that waiters do not wake
> before a notify is complete.
>
>
> std::mutex and condition_variable can't be implemented using only the
> guarantees in the standard provided by std::atomic either.
>
They don't need to be, because they're provided by the standard library.
>
> I'm not sure how concerning this is in practice, given pthreads (which is
> usually used to implement mutex) says as follows:
>
> The *pthread_mutex_destroy*() function shall destroy the mutex object
> referenced by *mutex*; the mutex object becomes, in effect,
> uninitialized. An implementation may cause *pthread_mutex_destroy*() to
> set the object referenced by *mutex* to an invalid value. A destroyed
> mutex object can be reinitialized using *pthread_mutex_init*(); the
> results of otherwise referencing the object after it has been destroyed are
> undefined.
>
> It shall be safe to destroy an initialized mutex that is unlocked.
> Attempting to destroy a locked mutex results in undefined behavior.
>
> Seems the pthread mutex implementation guarantees this is safe, but the
> C++ standard, as best I can tell, does not provide a way to implement
> either behavior using std::atomic.
>
I don't read it that way. In the abstract machine there is no time between
the "unlock" and the conceptual "notify" that happens to inform other
threads that they can lock it. It's not like a std::atomic.
If the thread that unlocks the mutex also destroys it, then you know the
unlock is sequenced before the destroy. If those two operations happen in
separate threads, you need external synchronization to ensure that the
unlock happens before the destroy (otherwise you can't know that the
destroy doesn't happen while the mutex is still locked, which would be UB).
Either way, the return from pthread_mutex_unlock happens before the
pthread_mutex_destroy, so this isn't comparable to the case you're
describing where you want to destroy an atomic concurrently with a notify
on that atomic (which is just user error as Andrey said in another reply -
don't do that).
>
> Looking at MUSL, it seems like it's using a very similar strategy to
> libc++ for implementing pthread_mutex_unlock();
>
> LeaveCriticalSection has the same behavior.
>
> It looks like all major implementations use this type of "mutate and
> notify" operation internally, but the standard library doesn't expose any
> way to do this which is not relying on what seems like UB.
>
> If there is something somewhere which defines this as working, I'm
> interested to know what it would be.
>
> I do not think you can (legally) implement std::mutex using std::atomic in
> a way that doesn't rely on these behaviors.
>
The standard doesn't need to provide a way to implement std::mutex at all.
>
> Alternative: publish a DR that clarifies that "atomic_notify_*" functions
> are well defined and do not cause a race even if the object no longer
> exists at the pointed address. I think this will work for recent Windows
> STL, libc++, Glibc, and MUSL, at least for most types. I haven't checked
> other implementations.
>
>
I would not support such a change.
Received on 2026-01-14 21:47:33
