futex.hpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * Distributed under the Boost Software License, Version 1.0.
  3. * (See accompanying file LICENSE_1_0.txt or copy at
  4. * http://www.boost.org/LICENSE_1_0.txt)
  5. *
  6. * Copyright (c) 2020 Andrey Semashev
  7. */
  8. /*!
  9. * \file atomic/detail/futex.hpp
  10. *
  11. * This header defines wrappers around futex syscall.
  12. *
  13. * http://man7.org/linux/man-pages/man2/futex.2.html
  14. * https://man.openbsd.org/futex
  15. */
  16. #ifndef BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_
  17. #define BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_
  18. #include <boost/atomic/detail/config.hpp>
  19. #ifdef BOOST_HAS_PRAGMA_ONCE
  20. #pragma once
  21. #endif
  22. #if defined(__linux__) || defined(__NETBSD__) || defined(__NetBSD__)
  23. #include <sys/syscall.h>
  24. #if defined(SYS_futex)
  25. #define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS_futex
  26. #elif defined(SYS_futex_time64)
  27. // On some 32-bit targets (e.g. riscv32) SYS_futex is not defined and instead SYS_futex_time64 is implemented,
  28. // which is equivalent to SYS_futex but uses 64-bit time_t.
  29. #define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS_futex_time64
  30. #elif defined(__NR_futex)
  31. // Some Android NDKs (Google NDK and older Crystax.NET NDK versions) don't define SYS_futex.
  32. #define BOOST_ATOMIC_DETAIL_SYS_FUTEX __NR_futex
  33. #elif defined(SYS___futex)
  34. // NetBSD defines SYS___futex, which has slightly different parameters. Basically, it has decoupled timeout and val2 parameters:
  35. // int __futex(int *addr1, int op, int val1, const struct timespec *timeout, int *addr2, int val2, int val3);
  36. // https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/syscall.h
  37. // http://bxr.su/NetBSD/sys/kern/sys_futex.c
  38. #define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS___futex
  39. #define BOOST_ATOMIC_DETAIL_NETBSD_FUTEX
  40. #endif
  41. #elif defined(__OpenBSD__)
  42. // OpenBSD provides futex(2) function wrapper since OpenBSD 6.2 (https://man.openbsd.org/OpenBSD-6.2/futex.2).
  43. // It has also removed syscall(2) interface:
  44. // https://github.com/openbsd/src/commit/cafeb892b121ee89c39c2b940e8ccd6950f50009
  45. #include <sys/param.h>
  46. #if OpenBSD >= 201711
  47. #define BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX
  48. #endif
  49. #endif
  50. #if defined(BOOST_ATOMIC_DETAIL_SYS_FUTEX) || defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX)
  51. #include <cstddef>
  52. #if defined(__linux__)
  53. #include <linux/futex.h>
  54. #else
  55. #include <sys/futex.h>
  56. #endif
  57. #include <boost/cstdint.hpp>
  58. #include <boost/atomic/detail/intptr.hpp>
  59. #include <boost/atomic/detail/header.hpp>
  60. #define BOOST_ATOMIC_DETAIL_HAS_FUTEX
  61. #if defined(FUTEX_PRIVATE_FLAG)
  62. #define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG FUTEX_PRIVATE_FLAG
  63. #elif defined(__ANDROID__)
  64. // On Android, futex.h is lacking many definitions, but the actual Linux kernel supports the API in full.
  65. #define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG 128
  66. #else
  67. #define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG 0
  68. #endif
  69. namespace boost {
  70. namespace atomics {
  71. namespace detail {
  72. //! Invokes an operation on the futex
  73. BOOST_FORCEINLINE int futex_invoke(void* addr1, int op, unsigned int val1, const void* timeout = NULL, void* addr2 = NULL, unsigned int val3 = 0) BOOST_NOEXCEPT
  74. {
  75. #if defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX)
  76. return ::futex
  77. (
  78. static_cast< volatile uint32_t* >(addr1),
  79. op,
  80. static_cast< int >(val1),
  81. static_cast< const struct timespec* >(timeout),
  82. static_cast< volatile uint32_t* >(addr2)
  83. );
  84. #elif defined(BOOST_ATOMIC_DETAIL_NETBSD_FUTEX)
  85. // Pass 0 in val2.
  86. return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, timeout, addr2, 0u, val3);
  87. #else
  88. return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, timeout, addr2, val3);
  89. #endif
  90. }
  91. //! Invokes an operation on the futex
  92. BOOST_FORCEINLINE int futex_invoke(void* addr1, int op, unsigned int val1, unsigned int val2, void* addr2 = NULL, unsigned int val3 = 0) BOOST_NOEXCEPT
  93. {
  94. #if defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX)
  95. return ::futex
  96. (
  97. static_cast< volatile uint32_t* >(addr1),
  98. op,
  99. static_cast< int >(val1),
  100. reinterpret_cast< const struct timespec* >(static_cast< atomics::detail::uintptr_t >(val2)),
  101. static_cast< volatile uint32_t* >(addr2)
  102. );
  103. #elif defined(BOOST_ATOMIC_DETAIL_NETBSD_FUTEX)
  104. // Pass NULL in timeout.
  105. return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, static_cast< void* >(NULL), addr2, val2, val3);
  106. #else
  107. return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, static_cast< atomics::detail::uintptr_t >(val2), addr2, val3);
  108. #endif
  109. }
  110. //! Checks that the value \c pval is \c expected and blocks
  111. BOOST_FORCEINLINE int futex_wait(void* pval, unsigned int expected) BOOST_NOEXCEPT
  112. {
  113. return futex_invoke(pval, FUTEX_WAIT, expected);
  114. }
  115. //! Checks that the value \c pval is \c expected and blocks
  116. BOOST_FORCEINLINE int futex_wait_private(void* pval, unsigned int expected) BOOST_NOEXCEPT
  117. {
  118. return futex_invoke(pval, FUTEX_WAIT | BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG, expected);
  119. }
  120. //! Wakes the specified number of threads waiting on the futex
  121. BOOST_FORCEINLINE int futex_signal(void* pval, unsigned int count = 1u) BOOST_NOEXCEPT
  122. {
  123. return futex_invoke(pval, FUTEX_WAKE, count);
  124. }
  125. //! Wakes the specified number of threads waiting on the futex
  126. BOOST_FORCEINLINE int futex_signal_private(void* pval, unsigned int count = 1u) BOOST_NOEXCEPT
  127. {
  128. return futex_invoke(pval, FUTEX_WAKE | BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG, count);
  129. }
  130. //! Wakes all threads waiting on the futex
  131. BOOST_FORCEINLINE int futex_broadcast(void* pval) BOOST_NOEXCEPT
  132. {
  133. return futex_signal(pval, (~static_cast< unsigned int >(0u)) >> 1);
  134. }
  135. //! Wakes all threads waiting on the futex
  136. BOOST_FORCEINLINE int futex_broadcast_private(void* pval) BOOST_NOEXCEPT
  137. {
  138. return futex_signal_private(pval, (~static_cast< unsigned int >(0u)) >> 1);
  139. }
  140. //! Wakes the wake_count threads waiting on the futex pval1 and requeues up to requeue_count of the blocked threads onto another futex pval2
  141. BOOST_FORCEINLINE int futex_requeue(void* pval1, void* pval2, unsigned int wake_count = 1u, unsigned int requeue_count = (~static_cast< unsigned int >(0u)) >> 1) BOOST_NOEXCEPT
  142. {
  143. return futex_invoke(pval1, FUTEX_REQUEUE, wake_count, requeue_count, pval2);
  144. }
  145. //! Wakes the wake_count threads waiting on the futex pval1 and requeues up to requeue_count of the blocked threads onto another futex pval2
  146. BOOST_FORCEINLINE int futex_requeue_private(void* pval1, void* pval2, unsigned int wake_count = 1u, unsigned int requeue_count = (~static_cast< unsigned int >(0u)) >> 1) BOOST_NOEXCEPT
  147. {
  148. return futex_invoke(pval1, FUTEX_REQUEUE | BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG, wake_count, requeue_count, pval2);
  149. }
  150. } // namespace detail
  151. } // namespace atomics
  152. } // namespace boost
  153. #include <boost/atomic/detail/footer.hpp>
  154. #endif // defined(BOOST_ATOMIC_DETAIL_SYS_FUTEX) || defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX)
  155. #endif // BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_