]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/third-party/folly/folly/detail/Futex.cpp
1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
6 #include <folly/detail/Futex.h>
7 #include <folly/portability/SysSyscall.h>
13 #include <folly/synchronization/ParkingLot.h>
16 #include <linux/futex.h>
23 using namespace std::chrono
;
30 ////////////////////////////////////////////////////
31 // native implementation using the futex() syscall
35 /// Certain toolchains (like Android's) don't include the full futex API in
36 /// their headers even though they support it. Make sure we have our constants
37 /// even if the headers don't have them.
38 #ifndef FUTEX_WAIT_BITSET
39 #define FUTEX_WAIT_BITSET 9
41 #ifndef FUTEX_WAKE_BITSET
42 #define FUTEX_WAKE_BITSET 10
44 #ifndef FUTEX_PRIVATE_FLAG
45 #define FUTEX_PRIVATE_FLAG 128
47 #ifndef FUTEX_CLOCK_REALTIME
48 #define FUTEX_CLOCK_REALTIME 256
51 int nativeFutexWake(const void* addr
, int count
, uint32_t wakeMask
) {
55 FUTEX_WAKE_BITSET
| FUTEX_PRIVATE_FLAG
, /* op */
57 nullptr, /* timeout */
61 /* NOTE: we ignore errors on wake for the case of a futex
62 guarding its own destruction, similar to this
63 glibc bug with sem_post/sem_wait:
64 https://sourceware.org/bugzilla/show_bug.cgi?id=12674 */
68 return static_cast<int>(rv
);
71 template <class Clock
>
72 struct timespec
timeSpecFromTimePoint(time_point
<Clock
> absTime
) {
73 auto epoch
= absTime
.time_since_epoch();
74 if (epoch
.count() < 0) {
75 // kernel timespec_valid requires non-negative seconds and nanos in [0,1G)
76 epoch
= Clock::duration::zero();
79 // timespec-safe seconds and nanoseconds;
80 // chrono::{nano,}seconds are `long long int`
81 // whereas timespec uses smaller types
82 using time_t_seconds
= duration
<std::time_t, seconds::period
>;
83 using long_nanos
= duration
<long int, nanoseconds::period
>;
85 auto secs
= duration_cast
<time_t_seconds
>(epoch
);
86 auto nanos
= duration_cast
<long_nanos
>(epoch
- secs
);
87 struct timespec result
= {secs
.count(), nanos
.count()};
91 FutexResult
nativeFutexWaitImpl(
94 system_clock::time_point
const* absSystemTime
,
95 steady_clock::time_point
const* absSteadyTime
,
97 assert(absSystemTime
== nullptr || absSteadyTime
== nullptr);
99 int op
= FUTEX_WAIT_BITSET
| FUTEX_PRIVATE_FLAG
;
101 struct timespec
* timeout
= nullptr;
103 if (absSystemTime
!= nullptr) {
104 op
|= FUTEX_CLOCK_REALTIME
;
105 ts
= timeSpecFromTimePoint(*absSystemTime
);
107 } else if (absSteadyTime
!= nullptr) {
108 ts
= timeSpecFromTimePoint(*absSteadyTime
);
112 // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET requires an absolute timeout
113 // value - http://locklessinc.com/articles/futex_cheat_sheet/
119 timeout
, /* timeout */
121 waitMask
); /* val3 */
124 return FutexResult::AWOKEN
;
128 assert(timeout
!= nullptr);
129 return FutexResult::TIMEDOUT
;
131 return FutexResult::INTERRUPTED
;
133 return FutexResult::VALUE_CHANGED
;
136 // EINVAL, EACCESS, or EFAULT. EINVAL means there was an invalid
137 // op (should be impossible) or an invalid timeout (should have
138 // been sanitized by timeSpecFromTimePoint). EACCESS or EFAULT
139 // means *addr points to invalid memory, which is unlikely because
140 // the caller should have segfaulted already. We can either
141 // crash, or return a value that lets the process continue for
142 // a bit. We choose the latter. VALUE_CHANGED probably turns the
143 // caller into a spin lock.
144 return FutexResult::VALUE_CHANGED
;
151 ///////////////////////////////////////////////////////
152 // compatibility implementation using standard C++ API
154 using Lot
= ParkingLot
<uint32_t>;
157 int emulatedFutexWake(const void* addr
, int count
, uint32_t waitMask
) {
159 parkingLot
.unpark(addr
, [&](const uint32_t& mask
) {
160 if ((mask
& waitMask
) == 0) {
161 return UnparkControl::RetainContinue
;
166 return count
> 0 ? UnparkControl::RemoveContinue
167 : UnparkControl::RemoveBreak
;
172 template <typename F
>
173 FutexResult
emulatedFutexWaitImpl(
176 system_clock::time_point
const* absSystemTime
,
177 steady_clock::time_point
const* absSteadyTime
,
180 std::is_same
<F
, const Futex
<std::atomic
>>::value
||
181 std::is_same
<F
, const Futex
<EmulatedFutexAtomic
>>::value
,
182 "Type F must be either Futex<std::atomic> or Futex<EmulatedFutexAtomic>");
185 res
= parkingLot
.park_until(
188 [&] { return *futex
== expected
; },
191 } else if (absSteadyTime
) {
192 res
= parkingLot
.park_until(
195 [&] { return *futex
== expected
; },
199 res
= parkingLot
.park(
200 futex
, waitMask
, [&] { return *futex
== expected
; }, [] {});
203 case ParkResult::Skip
:
204 return FutexResult::VALUE_CHANGED
;
205 case ParkResult::Unpark
:
206 return FutexResult::AWOKEN
;
207 case ParkResult::Timeout
:
208 return FutexResult::TIMEDOUT
;
211 return FutexResult::INTERRUPTED
;
216 /////////////////////////////////
220 const Futex
<std::atomic
>* futex
,
224 return nativeFutexWake(futex
, count
, wakeMask
);
226 return emulatedFutexWake(futex
, count
, wakeMask
);
231 const Futex
<EmulatedFutexAtomic
>* futex
,
234 return emulatedFutexWake(futex
, count
, wakeMask
);
237 FutexResult
futexWaitImpl(
238 const Futex
<std::atomic
>* futex
,
240 system_clock::time_point
const* absSystemTime
,
241 steady_clock::time_point
const* absSteadyTime
,
244 return nativeFutexWaitImpl(
245 futex
, expected
, absSystemTime
, absSteadyTime
, waitMask
);
247 return emulatedFutexWaitImpl(
248 futex
, expected
, absSystemTime
, absSteadyTime
, waitMask
);
252 FutexResult
futexWaitImpl(
253 const Futex
<EmulatedFutexAtomic
>* futex
,
255 system_clock::time_point
const* absSystemTime
,
256 steady_clock::time_point
const* absSteadyTime
,
258 return emulatedFutexWaitImpl(
259 futex
, expected
, absSystemTime
, absSteadyTime
, waitMask
);
262 } // namespace detail