futex.h 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright 2020 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
  15. #define ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
  16. #include "absl/base/config.h"
  17. #ifndef _WIN32
  18. #include <sys/time.h>
  19. #include <unistd.h>
  20. #endif
  21. #ifdef __linux__
  22. #include <linux/futex.h>
  23. #include <sys/syscall.h>
  24. #endif
  25. #include <errno.h>
  26. #include <stdio.h>
  27. #include <time.h>
  28. #include <atomic>
  29. #include <cstdint>
  30. #include <limits>
  31. #include "absl/base/optimization.h"
  32. #include "absl/synchronization/internal/kernel_timeout.h"
  33. #ifdef ABSL_INTERNAL_HAVE_FUTEX
  34. #error ABSL_INTERNAL_HAVE_FUTEX may not be set on the command line
  35. #elif defined(__BIONIC__)
  36. // Bionic supports all the futex operations we need even when some of the futex
  37. // definitions are missing.
  38. #define ABSL_INTERNAL_HAVE_FUTEX
  39. #elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME)
  40. // FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28.
  41. #define ABSL_INTERNAL_HAVE_FUTEX
  42. #endif
  43. #ifdef ABSL_INTERNAL_HAVE_FUTEX
  44. namespace absl {
  45. ABSL_NAMESPACE_BEGIN
  46. namespace synchronization_internal {
  47. // Some Android headers are missing these definitions even though they
  48. // support these futex operations.
  49. #ifdef __BIONIC__
  50. #ifndef SYS_futex
  51. #define SYS_futex __NR_futex
  52. #endif
  53. #ifndef FUTEX_WAIT_BITSET
  54. #define FUTEX_WAIT_BITSET 9
  55. #endif
  56. #ifndef FUTEX_PRIVATE_FLAG
  57. #define FUTEX_PRIVATE_FLAG 128
  58. #endif
  59. #ifndef FUTEX_CLOCK_REALTIME
  60. #define FUTEX_CLOCK_REALTIME 256
  61. #endif
  62. #ifndef FUTEX_BITSET_MATCH_ANY
  63. #define FUTEX_BITSET_MATCH_ANY 0xFFFFFFFF
  64. #endif
  65. #endif
  66. #if defined(__NR_futex_time64) && !defined(SYS_futex_time64)
  67. #define SYS_futex_time64 __NR_futex_time64
  68. #endif
  69. #if defined(SYS_futex_time64) && !defined(SYS_futex)
  70. #define SYS_futex SYS_futex_time64
  71. using FutexTimespec = struct timespec;
  72. #else
  73. // Some libc implementations have switched to an unconditional 64-bit `time_t`
  74. // definition. This means that `struct timespec` may not match the layout
  75. // expected by the kernel ABI on 32-bit platforms. So we define the
  76. // FutexTimespec that matches the kernel timespec definition. It should be safe
  77. // to use this struct for 64-bit userspace builds too, since it will use another
  78. // SYS_futex kernel call with 64-bit tv_sec inside timespec.
  79. struct FutexTimespec {
  80. long tv_sec; // NOLINT
  81. long tv_nsec; // NOLINT
  82. };
  83. #endif
  84. class FutexImpl {
  85. public:
  86. // Atomically check that `*v == val`, and if it is, then sleep until the until
  87. // woken by `Wake()`.
  88. static int Wait(std::atomic<int32_t>* v, int32_t val) {
  89. return WaitAbsoluteTimeout(v, val, nullptr);
  90. }
  91. // Atomically check that `*v == val`, and if it is, then sleep until
  92. // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`.
  93. static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val,
  94. const struct timespec* abs_timeout) {
  95. FutexTimespec ts;
  96. // https://locklessinc.com/articles/futex_cheat_sheet/
  97. // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
  98. auto err = syscall(
  99. SYS_futex, reinterpret_cast<int32_t*>(v),
  100. FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val,
  101. ToFutexTimespec(abs_timeout, &ts), nullptr, FUTEX_BITSET_MATCH_ANY);
  102. if (err != 0) {
  103. return -errno;
  104. }
  105. return 0;
  106. }
  107. // Atomically check that `*v == val`, and if it is, then sleep until
  108. // `*rel_timeout` has elapsed, or until woken by `Wake()`.
  109. static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val,
  110. const struct timespec* rel_timeout) {
  111. FutexTimespec ts;
  112. // Atomically check that the futex value is still 0, and if it
  113. // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
  114. auto err =
  115. syscall(SYS_futex, reinterpret_cast<int32_t*>(v), FUTEX_PRIVATE_FLAG,
  116. val, ToFutexTimespec(rel_timeout, &ts));
  117. if (err != 0) {
  118. return -errno;
  119. }
  120. return 0;
  121. }
  122. // Wakes at most `count` waiters that have entered the sleep state on `v`.
  123. static int Wake(std::atomic<int32_t>* v, int32_t count) {
  124. auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
  125. FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count);
  126. if (ABSL_PREDICT_FALSE(err < 0)) {
  127. return -errno;
  128. }
  129. return 0;
  130. }
  131. private:
  132. static FutexTimespec* ToFutexTimespec(const struct timespec* userspace_ts,
  133. FutexTimespec* futex_ts) {
  134. if (userspace_ts == nullptr) {
  135. return nullptr;
  136. }
  137. using FutexSeconds = decltype(futex_ts->tv_sec);
  138. using FutexNanoseconds = decltype(futex_ts->tv_nsec);
  139. constexpr auto kMaxSeconds{(std::numeric_limits<FutexSeconds>::max)()};
  140. if (userspace_ts->tv_sec > kMaxSeconds) {
  141. futex_ts->tv_sec = kMaxSeconds;
  142. } else {
  143. futex_ts->tv_sec = static_cast<FutexSeconds>(userspace_ts->tv_sec);
  144. }
  145. futex_ts->tv_nsec = static_cast<FutexNanoseconds>(userspace_ts->tv_nsec);
  146. return futex_ts;
  147. }
  148. };
  149. class Futex : public FutexImpl {};
  150. } // namespace synchronization_internal
  151. ABSL_NAMESPACE_END
  152. } // namespace absl
  153. #endif // ABSL_INTERNAL_HAVE_FUTEX
  154. #endif // ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_