logging: fix compiler compatibility for one more constexpr function
[folly.git] / folly / experimental / EventCount.h
1 /*
2  * Copyright 2017 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 <climits>
21 #include <thread>
22
23 #include <glog/logging.h>
24
25 #include <folly/Bits.h>
26 #include <folly/Likely.h>
27 #include <folly/detail/Futex.h>
28 #include <folly/portability/SysTime.h>
29 #include <folly/portability/Unistd.h>
30
31
32 namespace folly {
33
34 /**
35  * Event count: a condition variable for lock free algorithms.
36  *
37  * See http://www.1024cores.net/home/lock-free-algorithms/eventcounts for
38  * details.
39  *
40  * Event counts allow you to convert a non-blocking lock-free / wait-free
41  * algorithm into a blocking one, by isolating the blocking logic.  You call
42  * prepareWait() before checking your condition and then either cancelWait()
43  * or wait() depending on whether the condition was true.  When another
44  * thread makes the condition true, it must call notify() / notifyAll() just
45  * like a regular condition variable.
46  *
47  * If "<" denotes the happens-before relationship, consider 2 threads (T1 and
48  * T2) and 3 events:
49  * - E1: T1 returns from prepareWait
50  * - E2: T1 calls wait
51  *   (obviously E1 < E2, intra-thread)
52  * - E3: T2 calls notifyAll
53  *
54  * If E1 < E3, then E2's wait will complete (and T1 will either wake up,
55  * or not block at all)
56  *
57  * This means that you can use an EventCount in the following manner:
58  *
59  * Waiter:
60  *   if (!condition()) {  // handle fast path first
61  *     for (;;) {
62  *       auto key = eventCount.prepareWait();
63  *       if (condition()) {
64  *         eventCount.cancelWait();
65  *         break;
66  *       } else {
67  *         eventCount.wait(key);
68  *       }
69  *     }
70  *  }
71  *
72  *  (This pattern is encapsulated in await())
73  *
74  * Poster:
75  *   make_condition_true();
76  *   eventCount.notifyAll();
77  *
78  * Note that, just like with regular condition variables, the waiter needs to
79  * be tolerant of spurious wakeups and needs to recheck the condition after
80  * being woken up.  Also, as there is no mutual exclusion implied, "checking"
81  * the condition likely means attempting an operation on an underlying
82  * data structure (push into a lock-free queue, etc) and returning true on
83  * success and false on failure.
84  */
85 class EventCount {
86  public:
87   EventCount() noexcept : val_(0) { }
88
89   class Key {
90     friend class EventCount;
91     explicit Key(uint32_t e) noexcept : epoch_(e) { }
92     uint32_t epoch_;
93   };
94
95   void notify() noexcept;
96   void notifyAll() noexcept;
97   Key prepareWait() noexcept;
98   void cancelWait() noexcept;
99   void wait(Key key) noexcept;
100
101   /**
102    * Wait for condition() to become true.  Will clean up appropriately if
103    * condition() throws, and then rethrow.
104    */
105   template <class Condition>
106   void await(Condition condition);
107
108  private:
109   void doNotify(int n) noexcept;
110   EventCount(const EventCount&) = delete;
111   EventCount(EventCount&&) = delete;
112   EventCount& operator=(const EventCount&) = delete;
113   EventCount& operator=(EventCount&&) = delete;
114
115   // This requires 64-bit
116   static_assert(sizeof(int) == 4, "bad platform");
117   static_assert(sizeof(uint32_t) == 4, "bad platform");
118   static_assert(sizeof(uint64_t) == 8, "bad platform");
119   static_assert(sizeof(std::atomic<uint64_t>) == 8, "bad platform");
120   static_assert(sizeof(detail::Futex<std::atomic>) == 4, "bad platform");
121
122   static constexpr size_t kEpochOffset = kIsLittleEndian ? 1 : 0;
123
124   // val_ stores the epoch in the most significant 32 bits and the
125   // waiter count in the least significant 32 bits.
126   std::atomic<uint64_t> val_;
127
128   static constexpr uint64_t kAddWaiter = uint64_t(1);
129   static constexpr uint64_t kSubWaiter = uint64_t(-1);
130   static constexpr size_t  kEpochShift = 32;
131   static constexpr uint64_t kAddEpoch = uint64_t(1) << kEpochShift;
132   static constexpr uint64_t kWaiterMask = kAddEpoch - 1;
133 };
134
135 inline void EventCount::notify() noexcept {
136   doNotify(1);
137 }
138
139 inline void EventCount::notifyAll() noexcept {
140   doNotify(INT_MAX);
141 }
142
143 inline void EventCount::doNotify(int n) noexcept {
144   uint64_t prev = val_.fetch_add(kAddEpoch, std::memory_order_acq_rel);
145   if (UNLIKELY(prev & kWaiterMask)) {
146     (reinterpret_cast<detail::Futex<std::atomic>*>(&val_) + kEpochOffset)
147         ->futexWake(n);
148   }
149 }
150
151 inline EventCount::Key EventCount::prepareWait() noexcept {
152   uint64_t prev = val_.fetch_add(kAddWaiter, std::memory_order_acq_rel);
153   return Key(prev >> kEpochShift);
154 }
155
156 inline void EventCount::cancelWait() noexcept {
157   // memory_order_relaxed would suffice for correctness, but the faster
158   // #waiters gets to 0, the less likely it is that we'll do spurious wakeups
159   // (and thus system calls).
160   uint64_t prev = val_.fetch_add(kSubWaiter, std::memory_order_seq_cst);
161   DCHECK_NE((prev & kWaiterMask), 0);
162 }
163
164 inline void EventCount::wait(Key key) noexcept {
165   while ((val_.load(std::memory_order_acquire) >> kEpochShift) == key.epoch_) {
166     (reinterpret_cast<detail::Futex<std::atomic>*>(&val_) + kEpochOffset)
167         ->futexWait(key.epoch_);
168   }
169   // memory_order_relaxed would suffice for correctness, but the faster
170   // #waiters gets to 0, the less likely it is that we'll do spurious wakeups
171   // (and thus system calls)
172   uint64_t prev = val_.fetch_add(kSubWaiter, std::memory_order_seq_cst);
173   DCHECK_NE((prev & kWaiterMask), 0);
174 }
175
176 template <class Condition>
177 void EventCount::await(Condition condition) {
178   if (condition()) return;  // fast path
179
180   // condition() is the only thing that may throw, everything else is
181   // noexcept, so we can hoist the try/catch block outside of the loop
182   try {
183     for (;;) {
184       auto key = prepareWait();
185       if (condition()) {
186         cancelWait();
187         break;
188       } else {
189         wait(key);
190       }
191     }
192   } catch (...) {
193     cancelWait();
194     throw;
195   }
196 }
197
198 }  // namespace folly