template<typename _Tp, typename _ValFn>
_Tp
- _M_setup_wait(const _Tp* __addr, _ValFn __vfn,
- __wait_result_type __res = {})
+ _M_setup_wait(const _Tp* __addr, _ValFn __vfn)
{
static_assert(is_same_v<_Tp, decay_t<decltype(__vfn())>>);
- if (__res._M_has_val) // A previous wait loaded a recent value.
- {
- _M_old = __res._M_val;
- if constexpr (!__platform_wait_uses_type<_Tp>)
- {
- // __res._M_val might be the value of a proxy wait object,
- // not the value of *__addr. Call __vfn() to get new value.
- return __vfn();
- }
- // Not a proxy wait, so the value in __res._M_val was loaded
- // from *__addr and we don't need to call __vfn().
- else if constexpr (sizeof(_Tp) == sizeof(__UINT32_TYPE__))
- return __builtin_bit_cast(_Tp, (__UINT32_TYPE__)_M_old);
- else if constexpr (sizeof(_Tp) == sizeof(__UINT64_TYPE__))
- return __builtin_bit_cast(_Tp, (__UINT64_TYPE__)_M_old);
- else
- static_assert(false); // Unsupported size
- }
-
if constexpr (!__platform_wait_uses_type<_Tp>)
if (_M_setup_proxy_wait(__addr))
{
// We will use a proxy wait for this object.
- // The library has set _M_obj and _M_obj_size and _M_old.
+ // The library has set _M_wait_state, _M_obj, _M_obj_size,
+ // and _M_old.
// Call __vfn to load the current value from *__addr
// (which must happen after the call to _M_setup_proxy_wait).
return __vfn();
}
// We will use a futex-like operation to wait on this object,
- // and so can just load the value and store it into _M_old.
- auto __val = __vfn();
+ // so just load the value, store it into _M_old, and return it.
+ return _M_store(__vfn());
+ }
+
+ // Called after a wait returns, to prepare to wait again.
+ template<typename _Tp, typename _ValFn>
+ _Tp
+ _M_on_wake(const _Tp* __addr, _ValFn __vfn, __wait_result_type __res)
+ {
+ if constexpr (!__platform_wait_uses_type<_Tp>) // maybe a proxy wait
+ if (_M_obj != __addr) // definitely a proxy wait
+ {
+ if (__res._M_has_val)
+ // Previous wait loaded a recent value from the proxy.
+ _M_old = __res._M_val;
+ else // Load a new value from the proxy and store in _M_old.
+ (void) _M_setup_proxy_wait(nullptr);
+ // Read the current value of *__addr
+ return __vfn();
+ }
+
+ if (__res._M_has_val) // The previous wait loaded a recent value.
+ {
+ _M_old = __res._M_val;
+
+ // Not a proxy wait, so the value in __res._M_val was loaded
+ // from *__addr and we don't need to call __vfn().
+ if constexpr (sizeof(_Tp) == sizeof(__UINT64_TYPE__))
+ return __builtin_bit_cast(_Tp, (__UINT64_TYPE__)_M_old);
+ else if constexpr (sizeof(_Tp) == sizeof(__UINT32_TYPE__))
+ return __builtin_bit_cast(_Tp, (__UINT32_TYPE__)_M_old);
+ else if constexpr (sizeof(_Tp) == sizeof(__UINT16_TYPE__))
+ return __builtin_bit_cast(_Tp, (__UINT16_TYPE__)_M_old);
+ else if constexpr (sizeof(_Tp) == sizeof(__UINT8_TYPE__))
+ return __builtin_bit_cast(_Tp, (__UINT8_TYPE__)_M_old);
+ else // Should be a proxy wait for this size!
+ __glibcxx_assert(false);
+ }
+ else
+ return _M_store(__vfn());
+ }
+
+ private:
+ // Store __val in _M_old.
+ // pre: This must be a non-proxy wait.
+ template<typename _Tp>
+ [[__gnu__::__always_inline__]]
+ _Tp
+ _M_store(_Tp __val)
+ {
// We have to consider various sizes, because a future libstdc++.so
// might enable non-proxy waits for additional sizes.
if constexpr (sizeof(_Tp) == sizeof(__UINT64_TYPE__))
_M_old = __builtin_bit_cast(__UINT16_TYPE__, __val);
else if constexpr (sizeof(_Tp) == sizeof(__UINT8_TYPE__))
_M_old = __builtin_bit_cast(__UINT8_TYPE__, __val);
- else // _M_setup_proxy_wait should have returned true for this type!
+ else // Should be a proxy wait for this size!
__glibcxx_assert(false);
return __val;
}
- private:
// Returns true if a proxy wait will be used for __addr, false otherwise.
// If true, _M_wait_state, _M_obj, _M_obj_size, and _M_old are set.
// If false, data members are unchanged.
while (!__pred(__val))
{
auto __res = __detail::__wait_impl(__addr, __args);
- __val = __args._M_setup_wait(__addr, __vfn, __res);
+ __val = __args._M_on_wake(__addr, __vfn, __res);
}
// C++26 will return __val
}
// Return false (and don't change any data members) if we can do a non-proxy
// wait for the object of size `_M_obj_size` at address `addr`.
-// Otherwise, the object at addr needs to use a proxy wait. Set _M_wait_state,
-// set _M_obj and _M_obj_size to refer to the _M_wait_state->_M_ver proxy,
-// load the current value from _M_obj and store it in _M_old, then return true.
+// Otherwise, we will use a proxy wait. If addr == _M_obj this is the initial
+// setup of the proxy wait, so set _M_wait_state to the proxy state for addr,
+// and set _M_obj and _M_obj_size to refer to the _M_wait_state->_M_ver proxy.
+// For both the initial setup of a proxy wait and for subsequent calls to this
+// function for proxy waits, we load the current value from _M_obj (the proxy)
+// and store it in _M_old, then return true.
bool
__wait_args::_M_setup_proxy_wait(const void* addr)
{
- if (!use_proxy_wait(*this, addr)) // We can wait on this address directly.
+ __waitable_state* state = nullptr;
+
+ if (addr == _M_obj)
{
- // Ensure the caller set _M_obj correctly, as that's what we'll wait on:
- __glibcxx_assert(_M_obj == addr);
- return false;
- }
+ if (!use_proxy_wait(*this, addr)) // We can wait on this address directly.
+ return false;
- // This will be a proxy wait, so get a waitable state.
- auto state = set_wait_state(addr, *this);
+ // This will be a proxy wait, so get a waitable state.
+ state = set_wait_state(addr, *this);
- // The address we will wait on is the version count of the waitable state:
- _M_obj = &state->_M_ver;
- // __wait_impl and __wait_until_impl need to know this size:
- _M_obj_size = sizeof(state->_M_ver);
+ // The address we will wait on is the version count of the waitable state:
+ _M_obj = &state->_M_ver;
+ // __wait_impl and __wait_until_impl need to know this size:
+ _M_obj_size = sizeof(state->_M_ver);
+ }
+ else // This is not the first call to this function, already a proxy wait.
+ state = static_cast<__waitable_state*>(_M_wait_state);
// Read the value of the _M_ver counter.
_M_old = __atomic_load_n(&state->_M_ver, __ATOMIC_ACQUIRE);