On Thu, Nov 27, 2025 at 4:35 PM Jonathan Wakely <[email protected]> wrote:

> The __futex_wait_flags scoped enum doesn't really have any benefit in
> this file, because this code is no longer in the <atomic> header and so
> we don't need to worry so much about namespace pollution. Just defining
> the constants as int (and locally in the functions where they're needed)
> avoids needing a static_cast<int> from the enum type.
>
> I also noticed that _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE was never defined,
> which meant we never used the FUTEX_PRIVATE_FLAG to tell the kernel that
> all futex ops are process-private.
>
> Also add comments and deleted definitions describing the API expected
> for targets that define _GLIBCXX_HAVE_PLATFORM_WAIT.
>
> libstdc++-v3/ChangeLog:
>
>         * src/c++20/atomic.cc: Document platform wait API.
>         (__futex_wait_flags): Remove enumeration type.
>         (futex_private_flag): Define constant for FUTEX_PRIVATE_FLAG.
>         (__platform_wait): Use local variables for futex op constants.
>         (__platform_notify): Likewise.
>         (__platform_wait_until): Likewise. Adjust parameter types for
>         consistency with __platform_wait.
> ---
>
> Tested x86_64-linux.
>
LGTM.

>
>  libstdc++-v3/src/c++20/atomic.cc | 98 ++++++++++++++++++++------------
>  1 file changed, 63 insertions(+), 35 deletions(-)
>
> diff --git a/libstdc++-v3/src/c++20/atomic.cc
> b/libstdc++-v3/src/c++20/atomic.cc
> index 80915617f0bf..fdd67d834768 100644
> --- a/libstdc++-v3/src/c++20/atomic.cc
> +++ b/libstdc++-v3/src/c++20/atomic.cc
> @@ -35,8 +35,8 @@
>  #include <bits/functional_hash.h>
>
>  #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
> +# include <sys/syscall.h> // SYS_futex
>  # include <unistd.h>
> -# include <syscall.h>
>  # include <sys/time.h> // timespec
>  # define _GLIBCXX_HAVE_PLATFORM_WAIT 1
>  #endif
> @@ -50,56 +50,84 @@ namespace __detail
>  {
>  namespace
>  {
> -#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
> -  enum class __futex_wait_flags : int
> -  {
> -#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE
> -    __private_flag = 128,
> -#else
> -    __private_flag = 0,
> -#endif
> -    __wait = 0,
> -    __wake = 1,
> -    __wait_bitset = 9,
> -    __wake_bitset = 10,
> -    __wait_private = __wait | __private_flag,
> -    __wake_private = __wake | __private_flag,
> -    __wait_bitset_private = __wait_bitset | __private_flag,
> -    __wake_bitset_private = __wake_bitset | __private_flag,
> -    __bitset_match_any = -1
> -  };
> +#ifndef _GLIBCXX_HAVE_PLATFORM_WAIT
> +
> +  // If _GLIBCXX_HAVE_PLATFORM_WAIT is defined then the following three
> +  // functions should be defined in terms of platform-specific wait/wake
> +  // primitives. The `obj_size` parameter is the size in bytes of the
> object
> +  // at `*addr` (used if the platform supports waiting on more than one
> size,
> +  // in which case `addr` would be cast to a different type).
> +
> +  // Deleted definitions are here to give better errors if these functions
> +  // are used when _GLIBCXX_HAVE_PLATFORM_WAIT is not defined.
> +
> +  // Wait until *addr != curr_val.
> +  // Once a thread is waiting, it will not unblock and notice the value
> +  // has changed unless explicitly notified using `__platform_notify`.
> +  void
> +  __platform_wait(const __platform_wait_t* addr,
> +                 __platform_wait_t curr_val,
> +                 int obj_size) = delete;
> +
> +  // Wake one thread that is waiting for `*addr` to change,
> +  // or all waiting threads if `wake_all` is true.
> +  void
> +  __platform_notify(const __platform_wait_t* addr,
> +                   bool wake_all,
> +                   int obj_size) = delete;
> +
> +  // As `__platform_wait` but with timeout.
> +  // Returns true if the wait ended before the timeout (which could be
> because
> +  // the value changed and __platform_notify was called, but could be
> because
> +  // the wait was interrupted by a signal, or just a spurious wake).
> +  // Returns false if the timeout was reached.
> +  bool
> +  __platform_wait_until(const __platform_wait_t* addr,
> +                       __platform_wait_t curr_val,
> +                       __wait_clock_t::time_point timeout,
> +                       int obj_size) = delete;
> +
> +#elif defined _GLIBCXX_HAVE_LINUX_FUTEX
> +
> +  const int futex_private_flag = 128;
>
>    void
>    __platform_wait(const int* addr, int val, int /* obj_size */) noexcept
>    {
> -    if (syscall(SYS_futex, addr,
> -               static_cast<int>(__futex_wait_flags::__wait_private),
> -               val, nullptr))
> +    const int futex_op_wait = 0;
> +    const int futex_op_wait_private = futex_op_wait | futex_private_flag;
> +
> +    if (syscall(SYS_futex, addr, futex_op_wait_private, val, nullptr))
>        if (errno != EAGAIN && errno != EINTR)
>         __throw_system_error(errno);
>    }
>
>    void
> -  __platform_notify(const int* addr, bool all, int) noexcept
> +  __platform_notify(const int* addr, bool all, int /* obj_size */)
> noexcept
>    {
> -    syscall(SYS_futex, addr,
> -           static_cast<int>(__futex_wait_flags::__wake_private),
> -           all ? INT_MAX : 1);
> +    const int futex_op_wake = 1;
> +    const int futex_op_wake_private = futex_op_wake | futex_private_flag;
> +
> +    syscall(SYS_futex, addr, futex_op_wake_private, all ? INT_MAX : 1);
>    }
>
>    // returns true if wait ended before timeout
>    bool
> -  __platform_wait_until(const __platform_wait_t* addr,
> -                       __platform_wait_t val,
> -                       const __wait_clock_t::time_point& __atime,
> +  __platform_wait_until(const int* addr, int val,
> +                       const __wait_clock_t::time_point& abs_time,
>                         int /* obj_size */) noexcept
>    {
> -    struct timespec timeout = chrono::__to_timeout_timespec(__atime);
> +    // FUTEX_WAIT expects a relative timeout, so must use
> FUTEX_WAIT_BITSET
> +    // for an absolute timeout.
> +    const int futex_op_wait_bitset = 9;
> +    const int futex_op_wait_bitset_private
> +      = futex_op_wait_bitset | futex_private_flag;
> +    const int futex_bitset_match_any = 0xffffffff;
>
> -    if (syscall(SYS_futex, addr,
> -
>  static_cast<int>(__futex_wait_flags::__wait_bitset_private),
> -               val, &timeout, nullptr,
> -               static_cast<int>(__futex_wait_flags::__bitset_match_any)))
> +    struct timespec timeout = chrono::__to_timeout_timespec(abs_time);
> +
> +    if (syscall(SYS_futex, addr, futex_op_wait_bitset_private, val,
> +               &timeout, nullptr, futex_bitset_match_any))
>        {
>         if (errno == ETIMEDOUT)
>           return false;
> @@ -108,7 +136,7 @@ namespace
>        }
>      return true;
>    }
> -#endif // HAVE_LINUX_FUTEX
> +#endif // HAVE_PLATFORM_WAIT
>
>    // The state used by atomic waiting and notifying functions.
>    struct __waitable_state
> --
> 2.51.1
>
>

Reply via email to