Coverage Report

Created: 2026-09-14 06:45

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