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
28namespace folly {
29namespace detail {
30
31enum 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 */
50template <template <typename> class Atom = std::atomic>
51using 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 */
58template <typename Futex>
59FutexResult
60futexWait(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 */
72template <
73 typename Futex,
74 class Clock,
75 class Duration = typename Clock::duration>
76FutexResult 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 */
91template <typename Futex>
92int 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. */
100template <typename T>
101struct 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