2 * Copyright 2013 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef FOLLY_EXPERIMENTAL_EVENTCOUNT_H_
18 #define FOLLY_EXPERIMENTAL_EVENTCOUNT_H_
22 #include <linux/futex.h>
33 inline int futex(int* uaddr, int op, int val, const timespec* timeout,
34 int* uaddr2, int val3) noexcept {
35 return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
41 * Event count: a condition variable for lock free algorithms.
43 * See http://www.1024cores.net/home/lock-free-algorithms/eventcounts for
46 * Event counts allow you to convert a non-blocking lock-free / wait-free
47 * algorithm into a blocking one, by isolating the blocking logic. You call
48 * prepareWait() before checking your condition and then either cancelWait()
49 * or wait() depending on whether the condition was true. When another
50 * thread makes the condition true, it must call notify() / notifyAll() just
51 * like a regular condition variable.
53 * If "<" denotes the happens-before relationship, consider 2 threads (T1 and
55 * - E1: T1 returns from prepareWait
57 * (obviously E1 < E2, intra-thread)
58 * - E3: T2 calls notifyAll
60 * If E1 < E3, then E2's wait will complete (and T1 will either wake up,
61 * or not block at all)
63 * This means that you can use an EventCount in the following manner:
66 * if (!condition()) { // handle fast path first
68 * auto key = eventCount.prepareWait();
70 * eventCount.cancelWait();
73 * eventCount.wait(key);
78 * (This pattern is encapsulated in await())
81 * make_condition_true();
82 * eventCount.notifyAll();
84 * Note that, just like with regular condition variables, the waiter needs to
85 * be tolerant of spurious wakeups and needs to recheck the condition after
86 * being woken up. Also, as there is no mutual exclusion implied, "checking"
87 * the condition likely means attempting an operation on an underlying
88 * data structure (push into a lock-free queue, etc) and returning true on
89 * success and false on failure.
93 EventCount() noexcept : epoch_(0), waiters_(0) { }
96 friend class EventCount;
97 explicit Key(int e) noexcept : epoch_(e) { }
101 void notify() noexcept;
102 void notifyAll() noexcept;
103 Key prepareWait() noexcept;
104 void cancelWait() noexcept;
105 void wait(Key key) noexcept;
108 * Wait for condition() to become true. Will clean up appropriately if
109 * condition() throws, and then rethrow.
111 template <class Condition>
112 void await(Condition condition);
115 void doNotify(int n) noexcept;
116 EventCount(const EventCount&) = delete;
117 EventCount(EventCount&&) = delete;
118 EventCount& operator=(const EventCount&) = delete;
119 EventCount& operator=(EventCount&&) = delete;
121 std::atomic<int> epoch_;
122 std::atomic<int> waiters_;
125 inline void EventCount::notify() noexcept {
129 inline void EventCount::notifyAll() noexcept {
133 inline void EventCount::doNotify(int n) noexcept {
134 // The order is important: epoch_ is incremented before waiters_ is checked.
135 // prepareWait() increments waiters_ before checking epoch_, so it is
136 // impossible to miss a wakeup.
139 detail::futex(reinterpret_cast<int*>(&epoch_), FUTEX_WAKE, n, nullptr,
144 inline EventCount::Key EventCount::prepareWait() noexcept {
149 inline void EventCount::cancelWait() noexcept {
153 inline void EventCount::wait(Key key) noexcept {
154 while (epoch_ == key.epoch_) {
155 detail::futex(reinterpret_cast<int*>(&epoch_), FUTEX_WAIT, key.epoch_,
156 nullptr, nullptr, 0);
161 template <class Condition>
162 void EventCount::await(Condition condition) {
163 if (condition()) return; // fast path
165 // condition() is the only thing that may throw, everything else is
166 // noexcept, so we can hoist the try/catch block outside of the loop
169 auto key = prepareWait();
185 #endif /* FOLLY_EXPERIMENTAL_EVENTCOUNT_H_ */