C++ Logo

std-proposals

Advanced search

Re: [std-proposals] atomic_compare_exchange_and_notify

From: Ryan P. Nicholl <rnicholl_at_[hidden]>
Date: Wed, 14 Jan 2026 20:42:38 +0000
> 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.

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.

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.

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.

--
Ryan P. Nicholl
Tel: (678)-358-7765
On Wednesday, January 14th, 2026 at 03:08, Jonathan Wakely <cxx_at_[hidden]> wrote:
> On Wed, 14 Jan 2026, 00:55 Ryan P. Nicholl via Std-Proposals, <std-proposals_at_[hidden]> wrote:
>
>> It has come to my attention that it isn't possible to implement data structures like std::latch or a uselock using the C++ atomic library without undefined behavior.
>>
>> Namely, std::latch guarantees that count_down doesn't cause a race even if there are waiters, this isn't possible to implement on the abstract machine unless the notification can be safely executed atomically with the value set. Ordinary notify_one has undefined behavior if it races with the destructor of the atomic object.
>
> That doesn't mean it isn't possible to implement. It means you have to be careful how you use it.
>
> 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.
>
>> Of course, it's possible to implement std::latch using things like SYS_futex, so this isn't impossible to implement per se, only impossible to implement using the C++ standard library without relying on undefined behavior.
>
> It's possible using FUTEX_WAKE_OP, but what about non-linux platforms?
>
>> I would propose adding atomic_compare_exchange_and_notify. This would have the same behavior as atomic compare_exchange followed by notify, except that it doesn't cause undefined behavior if a waiter unblocks on the new value and destroys the atomic, which could race with notify.
>
> How would that work for std::atomic<long long> which doesn't use a futex directly?
> I definitely think it would be nice to have a C++ type that exposes futex functionality directly with no additional overhead, but std::atomic is not that type.

Received on 2026-01-14 20:42:50