Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:31:55

0001 // Copyright 2020 The Abseil Authors.
0002 //
0003 // Licensed under the Apache License, Version 2.0 (the "License");
0004 // you may not use this file except in compliance with the License.
0005 // You may obtain a copy of the License at
0006 //
0007 //      https://www.apache.org/licenses/LICENSE-2.0
0008 //
0009 // Unless required by applicable law or agreed to in writing, software
0010 // distributed under the License is distributed on an "AS IS" BASIS,
0011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012 // See the License for the specific language governing permissions and
0013 // limitations under the License.
0014 #ifndef ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
0015 #define ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
0016 
0017 #include "absl/base/config.h"
0018 
0019 #ifndef _WIN32
0020 #include <sys/time.h>
0021 #include <unistd.h>
0022 #endif
0023 
0024 #ifdef __linux__
0025 #include <linux/futex.h>
0026 #include <sys/syscall.h>
0027 #endif
0028 
0029 #include <errno.h>
0030 #include <stdio.h>
0031 #include <time.h>
0032 
0033 #include <atomic>
0034 #include <cstdint>
0035 #include <limits>
0036 
0037 #include "absl/base/optimization.h"
0038 #include "absl/synchronization/internal/kernel_timeout.h"
0039 
0040 #ifdef ABSL_INTERNAL_HAVE_FUTEX
0041 #error ABSL_INTERNAL_HAVE_FUTEX may not be set on the command line
0042 #elif defined(__BIONIC__)
0043 // Bionic supports all the futex operations we need even when some of the futex
0044 // definitions are missing.
0045 #define ABSL_INTERNAL_HAVE_FUTEX
0046 #elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME)
0047 // FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28.
0048 #define ABSL_INTERNAL_HAVE_FUTEX
0049 #endif
0050 
0051 #ifdef ABSL_INTERNAL_HAVE_FUTEX
0052 
0053 namespace absl {
0054 ABSL_NAMESPACE_BEGIN
0055 namespace synchronization_internal {
0056 
0057 // Some Android headers are missing these definitions even though they
0058 // support these futex operations.
0059 #ifdef __BIONIC__
0060 #ifndef SYS_futex
0061 #define SYS_futex __NR_futex
0062 #endif
0063 #ifndef FUTEX_WAIT_BITSET
0064 #define FUTEX_WAIT_BITSET 9
0065 #endif
0066 #ifndef FUTEX_PRIVATE_FLAG
0067 #define FUTEX_PRIVATE_FLAG 128
0068 #endif
0069 #ifndef FUTEX_CLOCK_REALTIME
0070 #define FUTEX_CLOCK_REALTIME 256
0071 #endif
0072 #ifndef FUTEX_BITSET_MATCH_ANY
0073 #define FUTEX_BITSET_MATCH_ANY 0xFFFFFFFF
0074 #endif
0075 #endif
0076 
0077 #if defined(__NR_futex_time64) && !defined(SYS_futex_time64)
0078 #define SYS_futex_time64 __NR_futex_time64
0079 #endif
0080 
0081 #if defined(SYS_futex_time64) && !defined(SYS_futex)
0082 #define SYS_futex SYS_futex_time64
0083 using FutexTimespec = struct timespec;
0084 #else
0085 // Some libc implementations have switched to an unconditional 64-bit `time_t`
0086 // definition. This means that `struct timespec` may not match the layout
0087 // expected by the kernel ABI on 32-bit platforms. So we define the
0088 // FutexTimespec that matches the kernel timespec definition. It should be safe
0089 // to use this struct for 64-bit userspace builds too, since it will use another
0090 // SYS_futex kernel call with 64-bit tv_sec inside timespec.
0091 struct FutexTimespec {
0092   long tv_sec;   // NOLINT
0093   long tv_nsec;  // NOLINT
0094 };
0095 #endif
0096 
0097 class FutexImpl {
0098  public:
0099   // Atomically check that `*v == val`, and if it is, then sleep until the until
0100   // woken by `Wake()`.
0101   static int Wait(std::atomic<int32_t>* v, int32_t val) {
0102     return WaitAbsoluteTimeout(v, val, nullptr);
0103   }
0104 
0105   // Atomically check that `*v == val`, and if it is, then sleep until
0106   // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`.
0107   static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val,
0108                                  const struct timespec* abs_timeout) {
0109     FutexTimespec ts;
0110     // https://locklessinc.com/articles/futex_cheat_sheet/
0111     // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
0112     auto err = syscall(
0113         SYS_futex, reinterpret_cast<int32_t*>(v),
0114         FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val,
0115         ToFutexTimespec(abs_timeout, &ts), nullptr, FUTEX_BITSET_MATCH_ANY);
0116     if (err != 0) {
0117       return -errno;
0118     }
0119     return 0;
0120   }
0121 
0122   // Atomically check that `*v == val`, and if it is, then sleep until
0123   // `*rel_timeout` has elapsed, or until woken by `Wake()`.
0124   static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val,
0125                                  const struct timespec* rel_timeout) {
0126     FutexTimespec ts;
0127     // Atomically check that the futex value is still 0, and if it
0128     // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
0129     auto err =
0130         syscall(SYS_futex, reinterpret_cast<int32_t*>(v), FUTEX_PRIVATE_FLAG,
0131                 val, ToFutexTimespec(rel_timeout, &ts));
0132     if (err != 0) {
0133       return -errno;
0134     }
0135     return 0;
0136   }
0137 
0138   // Wakes at most `count` waiters that have entered the sleep state on `v`.
0139   static int Wake(std::atomic<int32_t>* v, int32_t count) {
0140     auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
0141                        FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count);
0142     if (ABSL_PREDICT_FALSE(err < 0)) {
0143       return -errno;
0144     }
0145     return 0;
0146   }
0147 
0148  private:
0149   static FutexTimespec* ToFutexTimespec(const struct timespec* userspace_ts,
0150                                         FutexTimespec* futex_ts) {
0151     if (userspace_ts == nullptr) {
0152       return nullptr;
0153     }
0154 
0155     using FutexSeconds = decltype(futex_ts->tv_sec);
0156     using FutexNanoseconds = decltype(futex_ts->tv_nsec);
0157 
0158     constexpr auto kMaxSeconds{(std::numeric_limits<FutexSeconds>::max)()};
0159     if (userspace_ts->tv_sec > kMaxSeconds) {
0160       futex_ts->tv_sec = kMaxSeconds;
0161     } else {
0162       futex_ts->tv_sec = static_cast<FutexSeconds>(userspace_ts->tv_sec);
0163     }
0164     futex_ts->tv_nsec = static_cast<FutexNanoseconds>(userspace_ts->tv_nsec);
0165     return futex_ts;
0166   }
0167 };
0168 
0169 class Futex : public FutexImpl {};
0170 
0171 }  // namespace synchronization_internal
0172 ABSL_NAMESPACE_END
0173 }  // namespace absl
0174 
0175 #endif  // ABSL_INTERNAL_HAVE_FUTEX
0176 
0177 #endif  // ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_