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;
EmulatedFutexAtomicEmulatedFutexAtomic103   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