proxygen
folly::EventCount Class Reference

#include <EventCount.h>

Classes

class  Key
 

Public Member Functions

 EventCount () noexcept
 
void notify () noexcept
 
void notifyAll () noexcept
 
Key prepareWait () noexcept
 
void cancelWait () noexcept
 
void wait (Key key) noexcept
 
template<class Condition >
void await (Condition condition)
 

Private Member Functions

void doNotify (int n) noexcept
 
 EventCount (const EventCount &)=delete
 
 EventCount (EventCount &&)=delete
 
EventCountoperator= (const EventCount &)=delete
 
EventCountoperator= (EventCount &&)=delete
 

Private Attributes

std::atomic< uint64_tval_
 

Static Private Attributes

static constexpr size_t kEpochOffset = kIsLittleEndian ? 1 : 0
 
static constexpr uint64_t kAddWaiter = uint64_t(1)
 
static constexpr uint64_t kSubWaiter = uint64_t(-1)
 
static constexpr size_t kEpochShift = 32
 
static constexpr uint64_t kAddEpoch = uint64_t(1) << kEpochShift
 
static constexpr uint64_t kWaiterMask = kAddEpoch - 1
 

Detailed Description

Event count: a condition variable for lock free algorithms.

See http://www.1024cores.net/home/lock-free-algorithms/eventcounts for details.

Event counts allow you to convert a non-blocking lock-free / wait-free algorithm into a blocking one, by isolating the blocking logic. You call prepareWait() before checking your condition and then either cancelWait() or wait() depending on whether the condition was true. When another thread makes the condition true, it must call notify() / notifyAll() just like a regular condition variable.

If "<" denotes the happens-before relationship, consider 2 threads (T1 and T2) and 3 events:

  • E1: T1 returns from prepareWait
  • E2: T1 calls wait (obviously E1 < E2, intra-thread)
  • E3: T2 calls notifyAll

If E1 < E3, then E2's wait will complete (and T1 will either wake up, or not block at all)

This means that you can use an EventCount in the following manner:

Waiter: if (!condition()) { // handle fast path first for (;;) { auto key = eventCount.prepareWait(); if (condition()) { eventCount.cancelWait(); break; } else { eventCount.wait(key); } } }

(This pattern is encapsulated in await())

Poster: make_condition_true(); eventCount.notifyAll();

Note that, just like with regular condition variables, the waiter needs to be tolerant of spurious wakeups and needs to recheck the condition after being woken up. Also, as there is no mutual exclusion implied, "checking" the condition likely means attempting an operation on an underlying data structure (push into a lock-free queue, etc) and returning true on success and false on failure.

Definition at line 84 of file EventCount.h.

Constructor & Destructor Documentation

folly::EventCount::EventCount ( )
inlinenoexcept

Definition at line 86 of file EventCount.h.

86 : val_(0) {}
std::atomic< uint64_t > val_
Definition: EventCount.h:125
folly::EventCount::EventCount ( const EventCount )
privatedelete
folly::EventCount::EventCount ( EventCount &&  )
privatedelete

Member Function Documentation

template<class Condition >
void folly::EventCount::await ( Condition  condition)

Wait for condition() to become true. Will clean up appropriately if condition() throws, and then rethrow.

Definition at line 177 of file EventCount.h.

References cancelWait(), prepareWait(), and wait().

177  {
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 }
void wait(Key key) noexcept
Definition: EventCount.h:163
void cancelWait() noexcept
Definition: EventCount.h:155
Key prepareWait() noexcept
Definition: EventCount.h:150
void folly::EventCount::cancelWait ( )
inlinenoexcept

Definition at line 155 of file EventCount.h.

References kSubWaiter, kWaiterMask, uint64_t, and val_.

Referenced by await(), folly::gen::detail::PMap< Predicate >::Generator< Value, Source, Input, Output >::ExecutionPipeline::predApplier(), and folly::gen::detail::ClosableMPMCQueue< InputDecayed >::writeUnlessClosed().

155  {
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 }
std::atomic< uint64_t > val_
Definition: EventCount.h:125
static constexpr uint64_t kWaiterMask
Definition: EventCount.h:131
static constexpr uint64_t kSubWaiter
Definition: EventCount.h:128
void folly::EventCount::doNotify ( int  n)
inlineprivatenoexcept

Definition at line 142 of file EventCount.h.

References folly::detail::futexWake(), kAddEpoch, kEpochOffset, kWaiterMask, uint64_t, UNLIKELY, and val_.

Referenced by notify(), and notifyAll().

142  {
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 }
std::atomic< uint64_t > val_
Definition: EventCount.h:125
static constexpr uint64_t kAddEpoch
Definition: EventCount.h:130
static constexpr size_t kEpochOffset
Definition: EventCount.h:121
static constexpr uint64_t kWaiterMask
Definition: EventCount.h:131
#define UNLIKELY(x)
Definition: Likely.h:48
int futexWake(const Futex *futex, int count, uint32_t wakeMask)
Definition: Futex-inl.h:107
EventCount& folly::EventCount::operator= ( const EventCount )
privatedelete
EventCount& folly::EventCount::operator= ( EventCount &&  )
privatedelete
EventCount::Key folly::EventCount::prepareWait ( )
inlinenoexcept

Definition at line 150 of file EventCount.h.

References kAddWaiter, kEpochShift, folly::EventCount::Key::Key(), uint64_t, and val_.

Referenced by await(), folly::gen::detail::PMap< Predicate >::Generator< Value, Source, Input, Output >::ExecutionPipeline::predApplier(), folly::gen::detail::ClosableMPMCQueue< InputDecayed >::readUnlessClosed(), and folly::gen::detail::ClosableMPMCQueue< InputDecayed >::writeUnlessClosed().

150  {
151  uint64_t prev = val_.fetch_add(kAddWaiter, std::memory_order_acq_rel);
152  return Key(prev >> kEpochShift);
153 }
internal::KeyMatcher< M > Key(M inner_matcher)
std::atomic< uint64_t > val_
Definition: EventCount.h:125
static constexpr size_t kEpochShift
Definition: EventCount.h:129
static constexpr uint64_t kAddWaiter
Definition: EventCount.h:127
void folly::EventCount::wait ( Key  key)
inlinenoexcept

Definition at line 163 of file EventCount.h.

References folly::detail::futexWait(), kEpochOffset, kEpochShift, kSubWaiter, kWaiterMask, uint64_t, and val_.

Referenced by await(), folly::gen::detail::PMap< Predicate >::Generator< Value, Source, Input, Output >::ExecutionPipeline::predApplier(), folly::gen::detail::ClosableMPMCQueue< InputDecayed >::readUnlessClosed(), and folly::gen::detail::ClosableMPMCQueue< InputDecayed >::writeUnlessClosed().

163  {
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 }
std::atomic< uint64_t > val_
Definition: EventCount.h:125
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
static constexpr uint64_t kWaiterMask
Definition: EventCount.h:131
static constexpr uint64_t kSubWaiter
Definition: EventCount.h:128

Member Data Documentation

constexpr uint64_t folly::EventCount::kAddEpoch = uint64_t(1) << kEpochShift
staticprivate

Definition at line 130 of file EventCount.h.

Referenced by doNotify().

constexpr uint64_t folly::EventCount::kAddWaiter = uint64_t(1)
staticprivate

Definition at line 127 of file EventCount.h.

Referenced by prepareWait().

constexpr size_t folly::EventCount::kEpochOffset = kIsLittleEndian ? 1 : 0
staticprivate

Definition at line 121 of file EventCount.h.

Referenced by doNotify(), and wait().

constexpr size_t folly::EventCount::kEpochShift = 32
staticprivate

Definition at line 129 of file EventCount.h.

Referenced by prepareWait(), and wait().

constexpr uint64_t folly::EventCount::kSubWaiter = uint64_t(-1)
staticprivate

Definition at line 128 of file EventCount.h.

Referenced by cancelWait(), and wait().

constexpr uint64_t folly::EventCount::kWaiterMask = kAddEpoch - 1
staticprivate

Definition at line 131 of file EventCount.h.

Referenced by cancelWait(), doNotify(), and wait().

std::atomic<uint64_t> folly::EventCount::val_
private

Definition at line 125 of file EventCount.h.

Referenced by cancelWait(), doNotify(), prepareWait(), and wait().


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