1 | /* |
2 | * Copyright 2013-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #pragma once |
18 | |
19 | #include <atomic> |
20 | #include <cassert> |
21 | #include <chrono> |
22 | #include <cstdint> |
23 | #include <limits> |
24 | #include <type_traits> |
25 | |
26 | #include <folly/portability/Unistd.h> |
27 | |
28 | namespace folly { |
29 | namespace detail { |
30 | |
31 | enum class FutexResult { |
32 | VALUE_CHANGED, /* futex value didn't match expected */ |
33 | AWOKEN, /* wakeup by matching futex wake, or spurious wakeup */ |
34 | INTERRUPTED, /* wakeup by interrupting signal */ |
35 | TIMEDOUT, /* wakeup by expiring deadline */ |
36 | }; |
37 | |
38 | /** |
39 | * Futex is an atomic 32 bit unsigned integer that provides access to the |
40 | * futex() syscall on that value. It is templated in such a way that it |
41 | * can interact properly with DeterministicSchedule testing. |
42 | * |
43 | * If you don't know how to use futex(), you probably shouldn't be using |
44 | * this class. Even if you do know how, you should have a good reason |
45 | * (and benchmarks to back you up). |
46 | * |
47 | * Because of the semantics of the futex syscall, the futex family of |
48 | * functions are available as free functions rather than member functions |
49 | */ |
50 | template <template <typename> class Atom = std::atomic> |
51 | using Futex = Atom<std::uint32_t>; |
52 | |
53 | /** |
54 | * Puts the thread to sleep if this->load() == expected. Returns true when |
55 | * it is returning because it has consumed a wake() event, false for any |
56 | * other return (signal, this->load() != expected, or spurious wakeup). |
57 | */ |
58 | template <typename Futex> |
59 | FutexResult |
60 | futexWait(const Futex* futex, uint32_t expected, uint32_t waitMask = -1); |
61 | |
62 | /** |
63 | * Similar to futexWait but also accepts a deadline until when the wait call |
64 | * may block. |
65 | * |
66 | * Optimal clock types: std::chrono::system_clock, std::chrono::steady_clock. |
67 | * NOTE: On some systems steady_clock is just an alias for system_clock, |
68 | * and is not actually steady. |
69 | * |
70 | * For any other clock type, now() will be invoked twice. |
71 | */ |
72 | template < |
73 | typename Futex, |
74 | class Clock, |
75 | class Duration = typename Clock::duration> |
76 | FutexResult futexWaitUntil( |
77 | const Futex* futex, |
78 | uint32_t expected, |
79 | std::chrono::time_point<Clock, Duration> const& deadline, |
80 | uint32_t waitMask = -1); |
81 | |
82 | /** |
83 | * Wakes up to count waiters where (waitMask & wakeMask) != 0, returning the |
84 | * number of awoken threads, or -1 if an error occurred. Note that when |
85 | * constructing a concurrency primitive that can guard its own destruction, it |
86 | * is likely that you will want to ignore EINVAL here (as well as making sure |
87 | * that you never touch the object after performing the memory store that is |
88 | * the linearization point for unlock or control handoff). See |
89 | * https://sourceware.org/bugzilla/show_bug.cgi?id=13690 |
90 | */ |
91 | template <typename Futex> |
92 | int futexWake( |
93 | const Futex* futex, |
94 | int count = std::numeric_limits<int>::max(), |
95 | uint32_t wakeMask = -1); |
96 | |
97 | /** A std::atomic subclass that can be used to force Futex to emulate |
98 | * the underlying futex() syscall. This is primarily useful to test or |
99 | * benchmark the emulated implementation on systems that don't need it. */ |
100 | template <typename T> |
101 | struct EmulatedFutexAtomic : public std::atomic<T> { |
102 | EmulatedFutexAtomic() noexcept = default; |
103 | constexpr /* implicit */ EmulatedFutexAtomic(T init) noexcept |
104 | : std::atomic<T>(init) {} |
105 | // It doesn't copy or move |
106 | EmulatedFutexAtomic(EmulatedFutexAtomic&& rhs) = delete; |
107 | }; |
108 | |
109 | } // namespace detail |
110 | } // namespace folly |
111 | |
112 | #include <folly/detail/Futex-inl.h> |
113 | |