proxygen
folly::Baton< MayBlock, Atom > Class Template Reference

#include <Baton.h>

Public Member Functions

constexpr Baton () noexcept
 
 Baton (Baton const &)=delete
 
Batonoperator= (Baton const &)=delete
 
 ~Baton () noexcept
 
FOLLY_ALWAYS_INLINE bool ready () const noexcept
 
void reset () noexcept
 
void post () noexcept
 
FOLLY_ALWAYS_INLINE void wait (const WaitOptions &opt=wait_options()) noexcept
 
FOLLY_ALWAYS_INLINE bool try_wait () const noexcept
 
template<typename Rep , typename Period >
FOLLY_ALWAYS_INLINE bool try_wait_for (const std::chrono::duration< Rep, Period > &timeout, const WaitOptions &opt=wait_options()) noexcept
 
template<typename Clock , typename Duration >
FOLLY_ALWAYS_INLINE bool try_wait_until (const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt=wait_options()) noexcept
 
template<typename Rep , typename Period >
FOLLY_ALWAYS_INLINE bool timed_wait (const std::chrono::duration< Rep, Period > &timeout) noexcept
 Alias to try_wait_for. Deprecated. More...
 
template<typename Clock , typename Duration >
FOLLY_ALWAYS_INLINE bool timed_wait (const std::chrono::time_point< Clock, Duration > &deadline) noexcept
 Alias to try_wait_until. Deprecated. More...
 

Static Public Member Functions

static FOLLY_ALWAYS_INLINE WaitOptions wait_options ()
 

Private Types

enum  State : uint32_t {
  INIT = 0, EARLY_DELIVERY = 1, WAITING = 2, LATE_DELIVERY = 3,
  TIMED_OUT = 4
}
 

Private Member Functions

template<typename Clock , typename Duration >
FOLLY_NOINLINE bool tryWaitSlow (const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt) noexcept
 

Private Attributes

detail::Futex< Atomstate_
 

Detailed Description

template<bool MayBlock = true, template< typename > class Atom = std::atomic>
class folly::Baton< MayBlock, Atom >

A Baton allows a thread to block once and be awoken. Captures a single handoff, and during its lifecycle (from construction/reset to destruction/reset) a baton must either be post()ed and wait()ed exactly once each, or not at all.

Baton includes no internal padding, and is only 4 bytes in size. Any alignment or padding to avoid false sharing is up to the user.

This is basically a stripped-down semaphore that supports only a single call to sem_post and a single call to sem_wait.

The non-blocking version (MayBlock == false) provides more speed by using only load acquire and store release operations in the critical path, at the cost of disallowing blocking.

The current posix semaphore sem_t isn't too bad, but this provides more a bit more speed, inlining, smaller size, a guarantee that the implementation won't change, and compatibility with DeterministicSchedule. By having a much more restrictive lifecycle we can also add a bunch of assertions that can help to catch race conditions ahead of time.

Definition at line 56 of file Baton.h.

Member Enumeration Documentation

template<bool MayBlock = true, template< typename > class Atom = std::atomic>
enum folly::Baton::State : uint32_t
private
Enumerator
INIT 
EARLY_DELIVERY 
WAITING 
LATE_DELIVERY 
TIMED_OUT 

Definition at line 254 of file Baton.h.

Constructor & Destructor Documentation

template<bool MayBlock = true, template< typename > class Atom = std::atomic>
constexpr folly::Baton< MayBlock, Atom >::Baton ( )
inlinenoexcept

Definition at line 62 of file Baton.h.

References folly::Baton< MayBlock, Atom >::operator=().

62 : state_(INIT) {}
detail::Futex< Atom > state_
Definition: Baton.h:338
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
folly::Baton< MayBlock, Atom >::Baton ( Baton< MayBlock, Atom > const &  )
delete
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
folly::Baton< MayBlock, Atom >::~Baton ( )
inlinenoexcept

It is an error to destroy a Baton on which a thread is currently wait()ing. In practice this means that the waiter usually takes responsibility for destroying the Baton.

Definition at line 70 of file Baton.h.

References folly::Baton< MayBlock, Atom >::state_, and folly::Baton< MayBlock, Atom >::WAITING.

70  {
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  }
detail::Futex< Atom > state_
Definition: Baton.h:338

Member Function Documentation

template<bool MayBlock = true, template< typename > class Atom = std::atomic>
Baton& folly::Baton< MayBlock, Atom >::operator= ( Baton< MayBlock, Atom > const &  )
delete
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
void folly::Baton< MayBlock, Atom >::post ( )
inlinenoexcept

Causes wait() to wake up. For each lifetime of a Baton (where a lifetime starts at construction or reset() and ends at destruction or reset()) there can be at most one call to post(), in the single poster version. Any thread may call post().

Spin-only version

May-block versions

Definition at line 123 of file Baton.h.

References folly::Baton< MayBlock, Atom >::EARLY_DELIVERY, FOLLY_ALWAYS_INLINE, folly::detail::futexWake(), folly::Baton< MayBlock, Atom >::INIT, folly::Baton< MayBlock, Atom >::LATE_DELIVERY, folly::Baton< MayBlock, Atom >::state_, folly::Baton< MayBlock, Atom >::TIMED_OUT, uint32_t, and folly::Baton< MayBlock, Atom >::WAITING.

Referenced by folly::basicTest(), BENCHMARK(), BENCHMARK_RELATIVE(), folly::SingletonVault::doEagerInitVia(), futureExecutor(), folly::DefaultKeepAliveExecutor::keepAliveRelease(), poolStats(), RecursiveAddTest(), folly::test::run_basic_test(), folly::test::run_basic_timed_wait_tests(), folly::test::run_timed_wait_regular_test(), folly::test::run_try_wait_tests(), folly::EventBase::runInEventBaseThreadAndWait(), SimpleTest(), SlowpokeNeedySingleton::SlowpokeNeedySingleton(), TEST(), TEST_F(), ThreadExecutor::work(), and folly::ScopedEventBaseThread::~ScopedEventBaseThread().

123  {
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  }
detail::Futex< Atom > state_
Definition: Baton.h:338
int futexWake(const Futex *futex, int count, uint32_t wakeMask)
Definition: Futex-inl.h:107
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
FOLLY_ALWAYS_INLINE bool folly::Baton< MayBlock, Atom >::ready ( ) const
inlinenoexcept

Definition at line 87 of file Baton.h.

References folly::Baton< MayBlock, Atom >::EARLY_DELIVERY, folly::Baton< MayBlock, Atom >::INIT, LIKELY, s, and folly::Baton< MayBlock, Atom >::state_.

Referenced by folly::test::run_try_wait_tests(), folly::Baton< MayBlock, Atom >::try_wait(), and folly::Baton< MayBlock, Atom >::tryWaitSlow().

87  {
88  auto s = state_.load(std::memory_order_acquire);
89  assert(s == INIT || s == EARLY_DELIVERY);
90  return LIKELY(s == EARLY_DELIVERY);
91  }
#define LIKELY(x)
Definition: Likely.h:47
detail::Futex< Atom > state_
Definition: Baton.h:338
static set< string > s
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
void folly::Baton< MayBlock, Atom >::reset ( )
inlinenoexcept

Equivalent to destroying the Baton and creating a new one. It is a bug to call this while there is a waiting thread, so in practice the waiter will be the one that resets the baton.

Definition at line 96 of file Baton.h.

References folly::Baton< MayBlock, Atom >::INIT, folly::Baton< MayBlock, Atom >::state_, and folly::Baton< MayBlock, Atom >::WAITING.

Referenced by TEST(), and TEST_F().

96  {
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  }
detail::Futex< Atom > state_
Definition: Baton.h:338
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
template<typename Rep , typename Period >
FOLLY_ALWAYS_INLINE bool folly::Baton< MayBlock, Atom >::timed_wait ( const std::chrono::duration< Rep, Period > &  timeout)
inlinenoexcept

Alias to try_wait_for. Deprecated.

Definition at line 241 of file Baton.h.

References folly::Baton< MayBlock, Atom >::try_wait_for().

Referenced by TEST().

242  {
243  return try_wait_for(timeout);
244  }
FOLLY_ALWAYS_INLINE bool try_wait_for(const std::chrono::duration< Rep, Period > &timeout, const WaitOptions &opt=wait_options()) noexcept
Definition: Baton.h:206
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
template<typename Clock , typename Duration >
FOLLY_ALWAYS_INLINE bool folly::Baton< MayBlock, Atom >::timed_wait ( const std::chrono::time_point< Clock, Duration > &  deadline)
inlinenoexcept

Alias to try_wait_until. Deprecated.

Definition at line 248 of file Baton.h.

References folly::Baton< MayBlock, Atom >::try_wait_until().

249  {
250  return try_wait_until(deadline);
251  }
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
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
FOLLY_ALWAYS_INLINE bool folly::Baton< MayBlock, Atom >::try_wait ( ) const
inlinenoexcept

Similar to wait, but doesn't block the thread if it hasn't been posted.

try_wait has the following semantics:

  • It is ok to call try_wait any number times on the same baton until try_wait reports that the baton has been posted.
  • It is ok to call timed_wait or wait on the same baton if try_wait reports that baton hasn't been posted.
  • If try_wait indicates that the baton has been posted, it is invalid to call wait, try_wait or timed_wait on the same baton without resetting
Returns
true if baton has been posted, false othewise

Definition at line 190 of file Baton.h.

References folly::Baton< MayBlock, Atom >::ready().

Referenced by folly::test::run_try_wait_tests(), folly::Baton< MayBlock, Atom >::try_wait_for(), folly::Baton< MayBlock, Atom >::try_wait_until(), and folly::Baton< MayBlock, Atom >::wait().

190  {
191  return ready();
192  }
FOLLY_ALWAYS_INLINE bool ready() const noexcept
Definition: Baton.h:87
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
template<typename Rep , typename Period >
FOLLY_ALWAYS_INLINE bool folly::Baton< MayBlock, Atom >::try_wait_for ( const std::chrono::duration< Rep, Period > &  timeout,
const WaitOptions opt = wait_options() 
)
inlinenoexcept

Similar to wait, but with a timeout. The thread is unblocked if the timeout expires. Note: Only a single call to wait/try_wait_for/try_wait_until is allowed during a baton's life-cycle (from ctor/reset to dtor/reset). In other words, after try_wait_for the caller can't invoke wait/try_wait/try_wait_for/try_wait_until again on the same baton without resetting it.

Parameters
timeoutTime until which the thread can block
Returns
true if the baton was posted to before timeout, false otherwise

Definition at line 206 of file Baton.h.

References now(), folly::Baton< MayBlock, Atom >::try_wait(), and folly::Baton< MayBlock, Atom >::tryWaitSlow().

Referenced by TEST(), TEST_F(), and folly::Baton< MayBlock, Atom >::timed_wait().

208  {
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  }
std::chrono::steady_clock::time_point now()
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
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
template<typename Clock , typename Duration >
FOLLY_ALWAYS_INLINE bool folly::Baton< MayBlock, Atom >::try_wait_until ( const std::chrono::time_point< Clock, Duration > &  deadline,
const WaitOptions opt = wait_options() 
)
inlinenoexcept

Similar to wait, but with a deadline. The thread is unblocked if the deadline expires. Note: Only a single call to wait/try_wait_for/try_wait_until is allowed during a baton's life-cycle (from ctor/reset to dtor/reset). In other words, after try_wait_until the caller can't invoke wait/try_wait/try_wait_for/try_wait_until again on the same baton without resetting it.

Parameters
deadlineTime until which the thread can block
Returns
true if the baton was posted to before deadline, false otherwise

Definition at line 229 of file Baton.h.

References folly::Baton< MayBlock, Atom >::try_wait(), and folly::Baton< MayBlock, Atom >::tryWaitSlow().

Referenced by folly::test::run_basic_timed_wait_tests(), folly::test::run_timed_wait_regular_test(), folly::test::run_timed_wait_tmo_tests(), and folly::Baton< MayBlock, Atom >::timed_wait().

231  {
232  if (try_wait()) {
233  return true;
234  }
235 
236  return tryWaitSlow(deadline, opt);
237  }
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
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
template<typename Clock , typename Duration >
FOLLY_NOINLINE bool folly::Baton< MayBlock, Atom >::tryWaitSlow ( const std::chrono::time_point< Clock, Duration > &  deadline,
const WaitOptions opt 
)
inlineprivatenoexcept

Definition at line 263 of file Baton.h.

References folly::detail::advance, folly::Baton< MayBlock, Atom >::EARLY_DELIVERY, folly::detail::MemoryIdler::futexWaitUntil(), folly::Baton< MayBlock, Atom >::INIT, folly::Baton< MayBlock, Atom >::LATE_DELIVERY, max, folly::Baton< MayBlock, Atom >::ready(), s, folly::detail::spin_pause_until(), folly::detail::spin_yield_until(), folly::Baton< MayBlock, Atom >::state_, folly::detail::success, folly::Baton< MayBlock, Atom >::TIMED_OUT, folly::detail::TIMEDOUT, folly::detail::timeout, uint32_t, and folly::Baton< MayBlock, Atom >::WAITING.

Referenced by folly::Baton< MayBlock, Atom >::try_wait_for(), folly::Baton< MayBlock, Atom >::try_wait_until(), and folly::Baton< MayBlock, Atom >::wait().

265  {
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  }
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
LogLevel max
Definition: LogLevel.cpp:31
FOLLY_ALWAYS_INLINE bool ready() const noexcept
Definition: Baton.h:87
detail::Futex< Atom > state_
Definition: Baton.h:338
static set< string > s
spin_result spin_pause_until(std::chrono::time_point< Clock, Duration > const &deadline, WaitOptions const &opt, F f)
Definition: Spin.h:36
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
FOLLY_ALWAYS_INLINE void folly::Baton< MayBlock, Atom >::wait ( const WaitOptions opt = wait_options())
inlinenoexcept

Waits until post() has been called in the current Baton lifetime. May be called at most once during a Baton lifetime (construction |reset until destruction|reset). If post is called before wait in the current lifetime then this method returns immediately.

The restriction that there can be at most one wait() per lifetime could be relaxed somewhat without any perf or size regressions, but by making this condition very restrictive we can provide better checking in debug builds.

Definition at line 170 of file Baton.h.

References max, folly::Baton< MayBlock, Atom >::try_wait(), and folly::Baton< MayBlock, Atom >::tryWaitSlow().

Referenced by folly::basicTest(), BENCHMARK(), BENCHMARK_RELATIVE(), futureExecutor(), folly::DefaultKeepAliveExecutor::joinKeepAlive(), poolStats(), RecursiveAddTest(), folly::run(), folly::test::run_basic_test(), folly::EventBase::runInEventBaseThreadAndWait(), SimpleTest(), TEST(), TEST_F(), and ThreadExecutor::waitForStartup().

170  {
171  if (try_wait()) {
172  return;
173  }
174 
175  auto const deadline = std::chrono::steady_clock::time_point::max();
176  tryWaitSlow(deadline, opt);
177  }
LogLevel max
Definition: LogLevel.cpp:31
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
template<bool MayBlock = true, template< typename > class Atom = std::atomic>
static FOLLY_ALWAYS_INLINE WaitOptions folly::Baton< MayBlock, Atom >::wait_options ( )
inlinestatic

Definition at line 58 of file Baton.h.

58  {
59  return {};
60  }

Member Data Documentation

template<bool MayBlock = true, template< typename > class Atom = std::atomic>
detail::Futex<Atom> folly::Baton< MayBlock, Atom >::state_
private

The documentation for this class was generated from the following file: