You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[libc++] Make sure that we use the fast platform wait for all eligible types in atomic_wait #146145
While discussing about #109290 with @huixie90 and Apple folks, we noticed that we didn't use the underlying platform's API (e.g. __ulock_wait on Apple, futex on Linux) even in cases where we could. This is caused by the brittle way in which we distinguish between the platform-native contention state and other states (e.g. here):
We basically forward only atomics which are exactly of the right type to the fast path, and everything else gets the slow path. For example, on Apple __cxx_atomic_contention_t is int64_t. That means that if we try to wait on a std::atomic<uint64_t>, we'll end up in the slow path even though we could clearly use the underlying platform's wait mechanism, which is faster.
To resolve this, I would suggest re-thinking the ABI boundary of the synchronization library and basically forwarding (from the headers) any type that has a natural alignment and sizeof(T) == {32,64} to the underlying platform API. At the end of the day, we basically hand over a bag of bytes to the underlying platform wait, the value and the type of these bytes doesn't matter.
Now imagine user has two TU, A is compiled with the old libc++ and B is compiled with the new libc++.
A is calling atomic<int32_t>::wait, which ends up calling void notify(void*); in the dylib, which will wait on the global contention table
B is calling atomic<int32_t>::notify, which ends up calling void notify_new_api(in32_t*), which will notify the atomic itself.
So A and B won't work together properly because they don't operation on the same memory location.
to fix this, somehow we need to make sure the old void notify(void*); to do the right thing for int32_t, which is not trivial i think
Hmm, I think you're right. This is unlike most other similar situations because we have two APIs (wait and notify) and their implementation needs to be in sync in case they are called from different TUs because they use shared global state. This is pretty nasty.
The optimization does seem worth it to me though, I wonder how frequent/likely it is for people to be calling wait and notify from different TUs compiled against different versions of the library. That's probably rare. For now, I would suggest that we implement this new API but guard it behind an ABI macro and we can think about ways to roll it out without any ABI macro.
but then don't actually take advantage of that new information inside the dylib, unless an ABI macro is set. That allows us to make the changes to the headers unconditionally and manage our risk with deploying this improvement by only setting an ABI macro when building the dylib.
Activity
huixie90 commentedon Jun 29, 2025
I am not sure how we can get ABI stability. right now, we effectively have these 4 abi functions
imagine we add some new ABI boundary functions which can handle
int32_tand we keep the original functions unchanged.
Now imagine user has two TU, A is compiled with the old libc++ and B is compiled with the new libc++.
A is calling
atomic<int32_t>::wait, which ends up callingvoid notify(void*);in the dylib, which will wait on the global contention tableB is calling
atomic<int32_t>::notify, which ends up callingvoid notify_new_api(in32_t*), which will notify the atomic itself.So A and B won't work together properly because they don't operation on the same memory location.
to fix this, somehow we need to make sure the old
void notify(void*);to do the right thing forint32_t, which is not trivial i thinkldionne commentedon Jul 4, 2025
Hmm, I think you're right. This is unlike most other similar situations because we have two APIs (
waitandnotify) and their implementation needs to be in sync in case they are called from different TUs because they use shared global state. This is pretty nasty.The optimization does seem worth it to me though, I wonder how frequent/likely it is for people to be calling
waitandnotifyfrom different TUs compiled against different versions of the library. That's probably rare. For now, I would suggest that we implement this new API but guard it behind an ABI macro and we can think about ways to roll it out without any ABI macro.ldionne commentedon Jul 4, 2025
One strategy that we could potentially use is to make the changes we want to the headers:
but then don't actually take advantage of that new information inside the dylib, unless an ABI macro is set. That allows us to make the changes to the headers unconditionally and manage our risk with deploying this improvement by only setting an ABI macro when building the dylib.
[libc++] Allows any types of size 4 and 8 to use native platform uloc…
[libc++] Allows any types of size 4 and 8 to use native platform uloc…
huixie90 commentedon Dec 14, 2025
This is fixed by
#161086