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 17 #include "absl/base/config.h" 18 19 #ifndef _WIN32 20 #include <sys/time.h> 21 #include <unistd.h> 22 #endif 23 24 #ifdef __linux__ 25 #include <linux/futex.h> 26 #include <sys/syscall.h> 27 #endif 28 29 #include <errno.h> 30 #include <stdio.h> 31 #include <time.h> 32 33 #include <atomic> 34 #include <cstdint> 35 #include <limits> 36 37 #include "absl/base/optimization.h" 38 #include "absl/synchronization/internal/kernel_timeout.h" 39 40 #ifdef ABSL_INTERNAL_HAVE_FUTEX 41 #error ABSL_INTERNAL_HAVE_FUTEX may not be set on the command line 42 #elif defined(__BIONIC__) 43 // Bionic supports all the futex operations we need even when some of the futex 44 // definitions are missing. 45 #define ABSL_INTERNAL_HAVE_FUTEX 46 #elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME) 47 // FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28. 48 #define ABSL_INTERNAL_HAVE_FUTEX 49 #endif 50 51 #ifdef ABSL_INTERNAL_HAVE_FUTEX 52 53 namespace absl { 54 ABSL_NAMESPACE_BEGIN 55 namespace synchronization_internal { 56 57 // Some Android headers are missing these definitions even though they 58 // support these futex operations. 59 #ifdef __BIONIC__ 60 #ifndef SYS_futex 61 #define SYS_futex __NR_futex 62 #endif 63 #ifndef FUTEX_WAIT_BITSET 64 #define FUTEX_WAIT_BITSET 9 65 #endif 66 #ifndef FUTEX_PRIVATE_FLAG 67 #define FUTEX_PRIVATE_FLAG 128 68 #endif 69 #ifndef FUTEX_CLOCK_REALTIME 70 #define FUTEX_CLOCK_REALTIME 256 71 #endif 72 #ifndef FUTEX_BITSET_MATCH_ANY 73 #define FUTEX_BITSET_MATCH_ANY 0xFFFFFFFF 74 #endif 75 #endif 76 77 #if defined(__NR_futex_time64) && !defined(SYS_futex_time64) 78 #define SYS_futex_time64 __NR_futex_time64 79 #endif 80 81 #if defined(SYS_futex_time64) && !defined(SYS_futex) 82 #define SYS_futex SYS_futex_time64 83 using FutexTimespec = struct timespec; 84 #else 85 // Some libc implementations have switched to an unconditional 64-bit `time_t` 86 // definition. This means that `struct timespec` may not match the layout 87 // expected by the kernel ABI on 32-bit platforms. So we define the 88 // FutexTimespec that matches the kernel timespec definition. It should be safe 89 // to use this struct for 64-bit userspace builds too, since it will use another 90 // SYS_futex kernel call with 64-bit tv_sec inside timespec. 91 struct FutexTimespec { 92 long tv_sec; // NOLINT 93 long tv_nsec; // NOLINT 94 }; 95 #endif 96 97 class FutexImpl { 98 public: 99 // Atomically check that `*v == val`, and if it is, then sleep until the until 100 // woken by `Wake()`. Wait(std::atomic<int32_t> * v,int32_t val)101 static int Wait(std::atomic<int32_t>* v, int32_t val) { 102 return WaitAbsoluteTimeout(v, val, nullptr); 103 } 104 105 // Atomically check that `*v == val`, and if it is, then sleep until 106 // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`. WaitAbsoluteTimeout(std::atomic<int32_t> * v,int32_t val,const struct timespec * abs_timeout)107 static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val, 108 const struct timespec* abs_timeout) { 109 FutexTimespec ts; 110 // https://locklessinc.com/articles/futex_cheat_sheet/ 111 // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time. 112 auto err = syscall( 113 SYS_futex, reinterpret_cast<int32_t*>(v), 114 FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val, 115 ToFutexTimespec(abs_timeout, &ts), nullptr, FUTEX_BITSET_MATCH_ANY); 116 if (err != 0) { 117 return -errno; 118 } 119 return 0; 120 } 121 122 // Atomically check that `*v == val`, and if it is, then sleep until 123 // `*rel_timeout` has elapsed, or until woken by `Wake()`. WaitRelativeTimeout(std::atomic<int32_t> * v,int32_t val,const struct timespec * rel_timeout)124 static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val, 125 const struct timespec* rel_timeout) { 126 FutexTimespec ts; 127 // Atomically check that the futex value is still 0, and if it 128 // is, sleep until abs_timeout or until woken by FUTEX_WAKE. 129 auto err = 130 syscall(SYS_futex, reinterpret_cast<int32_t*>(v), FUTEX_PRIVATE_FLAG, 131 val, ToFutexTimespec(rel_timeout, &ts)); 132 if (err != 0) { 133 return -errno; 134 } 135 return 0; 136 } 137 138 // Wakes at most `count` waiters that have entered the sleep state on `v`. Wake(std::atomic<int32_t> * v,int32_t count)139 static int Wake(std::atomic<int32_t>* v, int32_t count) { 140 auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v), 141 FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count); 142 if (ABSL_PREDICT_FALSE(err < 0)) { 143 return -errno; 144 } 145 return 0; 146 } 147 148 private: ToFutexTimespec(const struct timespec * userspace_ts,FutexTimespec * futex_ts)149 static FutexTimespec* ToFutexTimespec(const struct timespec* userspace_ts, 150 FutexTimespec* futex_ts) { 151 if (userspace_ts == nullptr) { 152 return nullptr; 153 } 154 155 using FutexSeconds = decltype(futex_ts->tv_sec); 156 using FutexNanoseconds = decltype(futex_ts->tv_nsec); 157 158 constexpr auto kMaxSeconds{(std::numeric_limits<FutexSeconds>::max)()}; 159 if (userspace_ts->tv_sec > kMaxSeconds) { 160 futex_ts->tv_sec = kMaxSeconds; 161 } else { 162 futex_ts->tv_sec = static_cast<FutexSeconds>(userspace_ts->tv_sec); 163 } 164 futex_ts->tv_nsec = static_cast<FutexNanoseconds>(userspace_ts->tv_nsec); 165 return futex_ts; 166 } 167 }; 168 169 class Futex : public FutexImpl {}; 170 171 } // namespace synchronization_internal 172 ABSL_NAMESPACE_END 173 } // namespace absl 174 175 #endif // ABSL_INTERNAL_HAVE_FUTEX 176 177 #endif // ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_ 178