proxygen
EventCount.h
Go to the documentation of this file.
1 /*
2  * Copyright 2012-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 <climits>
21 #include <thread>
22 
23 #include <glog/logging.h>
24 
25 #include <folly/Likely.h>
26 #include <folly/detail/Futex.h>
27 #include <folly/lang/Bits.h>
30 
31 namespace folly {
32 
84 class EventCount {
85  public:
87 
88  class Key {
89  friend class EventCount;
90  explicit Key(uint32_t e) noexcept : epoch_(e) {}
92  };
93 
94  void notify() noexcept;
95  void notifyAll() noexcept;
96  Key prepareWait() noexcept;
97  void cancelWait() noexcept;
98  void wait(Key key) noexcept;
99 
104  template <class Condition>
105  void await(Condition condition);
106 
107  private:
108  void doNotify(int n) noexcept;
109  EventCount(const EventCount&) = delete;
110  EventCount(EventCount&&) = delete;
111  EventCount& operator=(const EventCount&) = delete;
112  EventCount& operator=(EventCount&&) = delete;
113 
114  // This requires 64-bit
115  static_assert(sizeof(int) == 4, "bad platform");
116  static_assert(sizeof(uint32_t) == 4, "bad platform");
117  static_assert(sizeof(uint64_t) == 8, "bad platform");
118  static_assert(sizeof(std::atomic<uint64_t>) == 8, "bad platform");
119  static_assert(sizeof(detail::Futex<std::atomic>) == 4, "bad platform");
120 
121  static constexpr size_t kEpochOffset = kIsLittleEndian ? 1 : 0;
122 
123  // val_ stores the epoch in the most significant 32 bits and the
124  // waiter count in the least significant 32 bits.
125  std::atomic<uint64_t> val_;
126 
127  static constexpr uint64_t kAddWaiter = uint64_t(1);
128  static constexpr uint64_t kSubWaiter = uint64_t(-1);
129  static constexpr size_t kEpochShift = 32;
130  static constexpr uint64_t kAddEpoch = uint64_t(1) << kEpochShift;
131  static constexpr uint64_t kWaiterMask = kAddEpoch - 1;
132 };
133 
134 inline void EventCount::notify() noexcept {
135  doNotify(1);
136 }
137 
139  doNotify(INT_MAX);
140 }
141 
142 inline void EventCount::doNotify(int n) noexcept {
143  uint64_t prev = val_.fetch_add(kAddEpoch, std::memory_order_acq_rel);
144  if (UNLIKELY(prev & kWaiterMask)) {
146  reinterpret_cast<detail::Futex<std::atomic>*>(&val_) + kEpochOffset, n);
147  }
148 }
149 
151  uint64_t prev = val_.fetch_add(kAddWaiter, std::memory_order_acq_rel);
152  return Key(prev >> kEpochShift);
153 }
154 
156  // memory_order_relaxed would suffice for correctness, but the faster
157  // #waiters gets to 0, the less likely it is that we'll do spurious wakeups
158  // (and thus system calls).
159  uint64_t prev = val_.fetch_add(kSubWaiter, std::memory_order_seq_cst);
160  DCHECK_NE((prev & kWaiterMask), 0);
161 }
162 
163 inline void EventCount::wait(Key key) noexcept {
164  while ((val_.load(std::memory_order_acquire) >> kEpochShift) == key.epoch_) {
166  reinterpret_cast<detail::Futex<std::atomic>*>(&val_) + kEpochOffset,
167  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()) {
179  return; // fast path
180  }
181 
182  // condition() is the only thing that may throw, everything else is
183  // noexcept, so we can hoist the try/catch block outside of the loop
184  try {
185  for (;;) {
186  auto key = prepareWait();
187  if (condition()) {
188  cancelWait();
189  break;
190  } else {
191  wait(key);
192  }
193  }
194  } catch (...) {
195  cancelWait();
196  throw;
197  }
198 }
199 
200 } // namespace folly
void notifyAll() noexcept
Definition: EventCount.h:138
Atom< std::uint32_t > Futex
Definition: Futex.h:51
std::atomic< uint64_t > val_
Definition: EventCount.h:125
STL namespace.
static constexpr uint64_t kAddEpoch
Definition: EventCount.h:130
constexpr auto kIsLittleEndian
Definition: Portability.h:278
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
FutexResult futexWait(const Futex *futex, uint32_t expected, uint32_t waitMask)
Definition: Futex-inl.h:100
static constexpr size_t kEpochShift
Definition: EventCount.h:129
static constexpr size_t kEpochOffset
Definition: EventCount.h:121
void wait(Key key) noexcept
Definition: EventCount.h:163
Key(uint32_t e) noexcept
Definition: EventCount.h:90
static constexpr uint64_t kAddWaiter
Definition: EventCount.h:127
static constexpr uint64_t kWaiterMask
Definition: EventCount.h:131
void doNotify(int n) noexcept
Definition: EventCount.h:142
folly::std enable_if::typetoAppendDelimStrImpl const Delimiter, const Tv, Tgtresult sizeof(Ts) >
static constexpr uint64_t kSubWaiter
Definition: EventCount.h:128
void cancelWait() noexcept
Definition: EventCount.h:155
const
Definition: upload.py:398
EventCount() noexcept
Definition: EventCount.h:86
void await(Condition condition)
Definition: EventCount.h:177
void notify() noexcept
Definition: EventCount.h:134
Key prepareWait() noexcept
Definition: EventCount.h:150
#define UNLIKELY(x)
Definition: Likely.h:48
int futexWake(const Futex *futex, int count, uint32_t wakeMask)
Definition: Futex-inl.h:107