proxygen
Baton.h
Go to the documentation of this file.
1 /*
2  * Copyright 2014-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 <assert.h>
20 #include <errno.h>
21 #include <stdint.h>
22 #include <atomic>
23 #include <thread>
24 
25 #include <folly/Likely.h>
26 #include <folly/detail/Futex.h>
28 #include <folly/portability/Asm.h>
31 
32 namespace folly {
33 
55 template <bool MayBlock = true, template <typename> class Atom = std::atomic>
56 class Baton {
57  public:
59  return {};
60  }
61 
62  constexpr Baton() noexcept : state_(INIT) {}
63 
64  Baton(Baton const&) = delete;
65  Baton& operator=(Baton const&) = delete;
66 
71  // The docblock for this function says that it can't be called when
72  // there is a concurrent waiter. We assume a strong version of this
73  // requirement in which the caller must _know_ that this is true, they
74  // are not allowed to be merely lucky. If two threads are involved,
75  // the destroying thread must actually have synchronized with the
76  // waiting thread after wait() returned. To convey causality the the
77  // waiting thread must have used release semantics and the destroying
78  // thread must have used acquire semantics for that communication,
79  // so we are guaranteed to see the post-wait() value of state_,
80  // which cannot be WAITING.
81  //
82  // Note that since we only care about a single memory location,
83  // the only two plausible memory orders here are relaxed and seq_cst.
84  assert(state_.load(std::memory_order_relaxed) != WAITING);
85  }
86 
88  auto s = state_.load(std::memory_order_acquire);
89  assert(s == INIT || s == EARLY_DELIVERY);
90  return LIKELY(s == EARLY_DELIVERY);
91  }
92 
96  void reset() noexcept {
97  // See ~Baton for a discussion about why relaxed is okay here
98  assert(state_.load(std::memory_order_relaxed) != WAITING);
99 
100  // We use a similar argument to justify the use of a relaxed store
101  // here. Since both wait() and post() are required to be called
102  // only once per lifetime, no thread can actually call those methods
103  // correctly after a reset() unless it synchronizes with the thread
104  // that performed the reset(). If a post() or wait() on another thread
105  // didn't synchronize, then regardless of what operation we performed
106  // here there would be a race on proper use of the Baton's spec
107  // (although not on any particular load and store). Put another way,
108  // we don't need to synchronize here because anybody that might rely
109  // on such synchronization is required by the baton rules to perform
110  // an additional synchronization that has the desired effect anyway.
111  //
112  // There is actually a similar argument to be made about the
113  // constructor, in which the fenceless constructor initialization
114  // of state_ is piggybacked on whatever synchronization mechanism
115  // distributes knowledge of the Baton's existence
116  state_.store(INIT, std::memory_order_relaxed);
117  }
118 
123  void post() noexcept {
124  if (!MayBlock) {
127  assert(
128  ((1 << state_.load(std::memory_order_relaxed)) &
129  ((1 << INIT) | (1 << EARLY_DELIVERY))) != 0);
130  state_.store(EARLY_DELIVERY, std::memory_order_release);
131  return;
132  }
133 
136  uint32_t before = state_.load(std::memory_order_acquire);
137 
138  assert(before == INIT || before == WAITING || before == TIMED_OUT);
139 
140  if (before == INIT &&
141  state_.compare_exchange_strong(
142  before,
144  std::memory_order_release,
145  std::memory_order_relaxed)) {
146  return;
147  }
148 
149  assert(before == WAITING || before == TIMED_OUT);
150 
151  if (before == TIMED_OUT) {
152  return;
153  }
154 
155  assert(before == WAITING);
156  state_.store(LATE_DELIVERY, std::memory_order_release);
158  }
159 
170  void wait(const WaitOptions& opt = wait_options()) noexcept {
171  if (try_wait()) {
172  return;
173  }
174 
175  auto const deadline = std::chrono::steady_clock::time_point::max();
176  tryWaitSlow(deadline, opt);
177  }
178 
191  return ready();
192  }
193 
205  template <typename Rep, typename Period>
207  const std::chrono::duration<Rep, Period>& timeout,
208  const WaitOptions& opt = wait_options()) noexcept {
209  if (try_wait()) {
210  return true;
211  }
212 
213  auto const deadline = std::chrono::steady_clock::now() + timeout;
214  return tryWaitSlow(deadline, opt);
215  }
216 
228  template <typename Clock, typename Duration>
230  const std::chrono::time_point<Clock, Duration>& deadline,
231  const WaitOptions& opt = wait_options()) noexcept {
232  if (try_wait()) {
233  return true;
234  }
235 
236  return tryWaitSlow(deadline, opt);
237  }
238 
240  template <typename Rep, typename Period>
242  const std::chrono::duration<Rep, Period>& timeout) noexcept {
243  return try_wait_for(timeout);
244  }
245 
247  template <typename Clock, typename Duration>
249  const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
250  return try_wait_until(deadline);
251  }
252 
253  private:
254  enum State : uint32_t {
255  INIT = 0,
257  WAITING = 2,
260  };
261 
262  template <typename Clock, typename Duration>
264  const std::chrono::time_point<Clock, Duration>& deadline,
265  const WaitOptions& opt) noexcept {
266  switch (detail::spin_pause_until(deadline, opt, [=] { return ready(); })) {
268  return true;
270  return false;
272  break;
273  }
274 
275  if (!MayBlock) {
276  switch (detail::spin_yield_until(deadline, [=] { return ready(); })) {
278  return true;
280  return false;
282  break;
283  }
284  }
285 
286  // guess we have to block :(
287  uint32_t expected = INIT;
288  if (!state_.compare_exchange_strong(
289  expected,
290  WAITING,
291  std::memory_order_relaxed,
292  std::memory_order_relaxed)) {
293  // CAS failed, last minute reprieve
294  assert(expected == EARLY_DELIVERY);
295  // TODO: move the acquire to the compare_exchange failure load after C++17
296  std::atomic_thread_fence(std::memory_order_acquire);
297  return true;
298  }
299 
300  while (true) {
301  auto rv = detail::MemoryIdler::futexWaitUntil(state_, WAITING, deadline);
302 
303  // Awoken by the deadline passing.
304  if (rv == detail::FutexResult::TIMEDOUT) {
305  assert(deadline != (std::chrono::time_point<Clock, Duration>::max()));
306  state_.store(TIMED_OUT, std::memory_order_release);
307  return false;
308  }
309 
310  // Probably awoken by a matching wake event, but could also by awoken
311  // by an asynchronous signal or by a spurious wakeup.
312  //
313  // state_ is the truth even if FUTEX_WAIT reported a matching
314  // FUTEX_WAKE, since we aren't using type-stable storage and we
315  // don't guarantee reuse. The scenario goes like this: thread
316  // A's last touch of a Baton is a call to wake(), which stores
317  // LATE_DELIVERY and gets an unlucky context switch before delivering
318  // the corresponding futexWake. Thread B sees LATE_DELIVERY
319  // without consuming a futex event, because it calls futexWait
320  // with an expected value of WAITING and hence doesn't go to sleep.
321  // B returns, so the Baton's memory is reused and becomes another
322  // Baton (or a reuse of this one). B calls futexWait on the new
323  // Baton lifetime, then A wakes up and delivers a spurious futexWake
324  // to the same memory location. B's futexWait will then report a
325  // consumed wake event even though state_ is still WAITING.
326  //
327  // It would be possible to add an extra state_ dance to communicate
328  // that the futexWake has been sent so that we can be sure to consume
329  // it before returning, but that would be a perf and complexity hit.
330  uint32_t s = state_.load(std::memory_order_acquire);
331  assert(s == WAITING || s == LATE_DELIVERY);
332  if (s == LATE_DELIVERY) {
333  return true;
334  }
335  }
336  }
337 
339 };
340 
341 } // namespace folly
static FutexResult futexWaitUntil(Futex &fut, uint32_t expected, Deadline const &deadline, uint32_t waitMask=-1, IdleTime const &idleTimeout=defaultIdleTimeout.load(std::memory_order_acquire), size_t stackToRetain=kDefaultStackToRetain, float timeoutVariationFrac=0.5)
Definition: MemoryIdler.h:139
spin_result spin_yield_until(std::chrono::time_point< Clock, Duration > const &deadline, F f)
Definition: Spin.h:70
#define FOLLY_ALWAYS_INLINE
Definition: CPortability.h:151
LogLevel max
Definition: LogLevel.cpp:31
std::chrono::steady_clock::time_point now()
Atom< std::uint32_t > Futex
Definition: Futex.h:51
#define LIKELY(x)
Definition: Likely.h:47
FOLLY_ALWAYS_INLINE bool timed_wait(const std::chrono::duration< Rep, Period > &timeout) noexcept
Alias to try_wait_for. Deprecated.
Definition: Baton.h:241
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
FOLLY_ALWAYS_INLINE bool try_wait_until(const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt=wait_options()) noexcept
Definition: Baton.h:229
Baton & operator=(Baton const &)=delete
FOLLY_ALWAYS_INLINE bool try_wait_for(const std::chrono::duration< Rep, Period > &timeout, const WaitOptions &opt=wait_options()) noexcept
Definition: Baton.h:206
#define FOLLY_NOINLINE
Definition: CPortability.h:142
FOLLY_ALWAYS_INLINE void wait(const WaitOptions &opt=wait_options()) noexcept
Definition: Baton.h:170
#define Atom
FOLLY_ALWAYS_INLINE bool ready() const noexcept
Definition: Baton.h:87
void post() noexcept
Definition: Baton.h:123
detail::Futex< Atom > state_
Definition: Baton.h:338
constexpr Baton() noexcept
Definition: Baton.h:62
static FOLLY_ALWAYS_INLINE WaitOptions wait_options()
Definition: Baton.h:58
static set< string > s
const
Definition: upload.py:398
FOLLY_ALWAYS_INLINE bool timed_wait(const std::chrono::time_point< Clock, Duration > &deadline) noexcept
Alias to try_wait_until. Deprecated.
Definition: Baton.h:248
FOLLY_ALWAYS_INLINE bool try_wait() const noexcept
Definition: Baton.h:190
FOLLY_NOINLINE bool tryWaitSlow(const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt) noexcept
Definition: Baton.h:263
~Baton() noexcept
Definition: Baton.h:70
spin_result spin_pause_until(std::chrono::time_point< Clock, Duration > const &deadline, WaitOptions const &opt, F f)
Definition: Spin.h:36
void reset() noexcept
Definition: Baton.h:96
int futexWake(const Futex *futex, int count, uint32_t wakeMask)
Definition: Futex-inl.h:107