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

#include <SaturatingSemaphore.h>

Public Member Functions

constexpr SaturatingSemaphore () noexcept
 
 ~SaturatingSemaphore ()
 
FOLLY_ALWAYS_INLINE bool ready () const noexcept
 
void reset () noexcept
 
FOLLY_ALWAYS_INLINE void post () noexcept
 
FOLLY_ALWAYS_INLINE void wait (const WaitOptions &opt=wait_options()) noexcept
 
FOLLY_ALWAYS_INLINE bool try_wait () 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<class Rep , class Period >
FOLLY_ALWAYS_INLINE bool try_wait_for (const std::chrono::duration< Rep, Period > &duration, const WaitOptions &opt=wait_options()) noexcept
 
template<typename Clock , typename Duration >
FOLLY_NOINLINE bool tryWaitSlow (const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt) noexcept
 

Static Public Member Functions

static FOLLY_ALWAYS_INLINE WaitOptions wait_options ()
 

Private Types

enum  State : uint32_t { NOTREADY = 0, READY = 1, BLOCKED = 2 }
 

Private Member Functions

FOLLY_ALWAYS_INLINE void postFastWaiterMayBlock () noexcept
 
void postSlowWaiterMayBlock (uint32_t before) noexcept
 
template<typename Clock , typename Duration >
bool tryWaitSlow (const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt) noexcept
 

Private Attributes

detail::Futex< Atomstate_
 

Detailed Description

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

SaturatingSemaphore is a flag that allows concurrent posting by multiple posters and concurrent non-destructive waiting by multiple waiters.

A SaturatingSemaphore allows one or more waiter threads to check, spin, or block, indefinitely or with timeout, for a flag to be set by one or more poster threads. By setting the flag, posters announce to waiters (that may be already waiting or will check the flag in the future) that some condition is true. Posts to an already set flag are idempotent.

SaturatingSemaphore is called so because it behaves like a hybrid binary/counted semaphore with values zero and infinity, and post() and wait() functions. It is called saturating because one post() is enough to set it to infinity and to satisfy any number of wait()-s. Once set (to infinity) it remains unchanged by subsequent post()-s and wait()-s, until it is reset() back to zero.

The implementation of SaturatingSemaphore is based on that of Baton. It includes no internal padding, and is only 4 bytes in size. Any alignment or padding to avoid false sharing is up to the user. SaturatingSemaphore differs from Baton as follows:

  • Baton allows at most one call to post(); this allows any number and concurrently.
  • Baton allows at most one successful call to any wait variant; this allows any number and concurrently.

Template parameter:

  • bool MayBlock: If false, waiting operations spin only. If true, timed and wait operations may block; adds an atomic instruction to the critical path of posters.

Wait options: WaitOptions contains optional per call setting for spin-max duration: Calls to wait(), try_wait_until(), and try_wait_for() block only after the passage of the spin-max period. The default spin-max duration is 10 usec. The spin-max option is applicable only if MayBlock is true.

Functions: bool ready(): Returns true if the flag is set by a call to post, otherwise false. Equivalent to try_wait, but available on const receivers. void reset(); Clears the flag. void post(); Sets the flag and wakes all current waiters, i.e., causes all concurrent calls to wait, try_wait_for, and try_wait_until to return. void wait( WaitOptions opt = wait_options()); Waits for the flag to be set by a call to post. bool try_wait(); Returns true if the flag is set by a call to post, otherwise false. bool try_wait_until( time_point& deadline, WaitOptions& = wait_options()); Returns true if the flag is set by a call to post before the deadline, otherwise false. bool try_wait_for( duration&, WaitOptions& = wait_options()); Returns true if the flag is set by a call to post before the expiration of the specified duration, otherwise false.

Usage:

SaturatingSemaphore</* MayBlock = */ true> f;
ASSERT_FALSE(f.try_wait());
ASSERT_FALSE(f.try_wait_until(
std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
ASSERT_FALSE(f.try_wait_until(
std::chrono::steady_clock::now() + std::chrono::microseconds(1),
f.wait_options().spin_max(std::chrono::microseconds(1))));
f.post();
f.post();
f.wait();
f.wait(f.wait_options().spin_max(std::chrono::nanoseconds(100)));
ASSERT_TRUE(f.try_wait());
ASSERT_TRUE(f.try_wait_until(
std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
f.wait();
f.reset();
ASSERT_FALSE(f.try_wait());

Definition at line 120 of file SaturatingSemaphore.h.

Member Enumeration Documentation

template<bool MayBlock, template< typename > class Atom = std::atomic>
enum folly::SaturatingSemaphore::State : uint32_t
private
Enumerator
NOTREADY 
READY 
BLOCKED 

Definition at line 123 of file SaturatingSemaphore.h.

Constructor & Destructor Documentation

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

constructor

Definition at line 135 of file SaturatingSemaphore.h.

template<bool MayBlock, template< typename > class Atom = std::atomic>
folly::SaturatingSemaphore< MayBlock, Atom >::~SaturatingSemaphore ( )
inline

destructor

Definition at line 138 of file SaturatingSemaphore.h.

138 {}

Member Function Documentation

template<bool MayBlock, template< typename > class Atom = std::atomic>
FOLLY_ALWAYS_INLINE void folly::SaturatingSemaphore< MayBlock, Atom >::postFastWaiterMayBlock ( )
inlineprivatenoexcept

Definition at line 194 of file SaturatingSemaphore.h.

Referenced by folly::SaturatingSemaphore< true, Atom >::post().

194  {
195  uint32_t before = NOTREADY;
196  if (LIKELY(state_.compare_exchange_strong(
197  before,
198  READY,
199  std::memory_order_release,
200  std::memory_order_relaxed))) {
201  return;
202  }
203  postSlowWaiterMayBlock(before);
204  }
#define LIKELY(x)
Definition: Likely.h:47
detail::Futex< Atom > state_
void postSlowWaiterMayBlock(uint32_t before) noexcept
template<bool MayBlock, template< typename > class Atom>
FOLLY_NOINLINE void folly::SaturatingSemaphore< MayBlock, Atom >::postSlowWaiterMayBlock ( uint32_t  before)
privatenoexcept

Member function definitioonspostSlowWaiterMayBlock

Definition at line 220 of file SaturatingSemaphore.h.

References folly::SaturatingSemaphore< MayBlock, Atom >::BLOCKED, folly::detail::futexWake(), folly::SaturatingSemaphore< MayBlock, Atom >::NOTREADY, and folly::SaturatingSemaphore< MayBlock, Atom >::READY.

Referenced by folly::SaturatingSemaphore< true, Atom >::postFastWaiterMayBlock().

221  {
222  while (true) {
223  if (before == NOTREADY) {
224  if (state_.compare_exchange_strong(
225  before,
226  READY,
227  std::memory_order_release,
228  std::memory_order_relaxed)) {
229  return;
230  }
231  }
232  if (before == READY) { // Only if multiple posters
233  // The reason for not simply returning (without the following
234  // steps) is to prevent the following case:
235  //
236  // T1: T2: T3:
237  // local1.post(); local2.post(); global.wait();
238  // global.post(); global.post(); global.reset();
239  // seq_cst fence
240  // local1.try_wait() == true;
241  // local2.try_wait() == false;
242  //
243  // This following steps correspond to T2's global.post(), where
244  // global is already posted by T1.
245  //
246  // The following fence and load guarantee that T3 does not miss
247  // T2's prior stores, i.e., local2.post() in this example.
248  //
249  // The following case is prevented:
250  //
251  // Starting with local2 == NOTREADY and global == READY
252  //
253  // T2: T3:
254  // store READY to local2 // post store NOTREADY to global // reset
255  // seq_cst fenc seq_cst fence
256  // load READY from global // post load NOTREADY from local2 // try_wait
257  //
258  std::atomic_thread_fence(std::memory_order_seq_cst);
259  before = state_.load(std::memory_order_relaxed);
260  if (before == READY) {
261  return;
262  }
263  continue;
264  }
265  DCHECK_EQ(before, BLOCKED);
266  if (state_.compare_exchange_strong(
267  before,
268  READY,
269  std::memory_order_release,
270  std::memory_order_relaxed)) {
272  return;
273  }
274  }
275 }
detail::Futex< Atom > state_
int futexWake(const Futex *futex, int count, uint32_t wakeMask)
Definition: Futex-inl.h:107
template<bool MayBlock, template< typename > class Atom = std::atomic>
FOLLY_ALWAYS_INLINE bool folly::SaturatingSemaphore< MayBlock, Atom >::try_wait ( )
inlinenoexcept
template<bool MayBlock, template< typename > class Atom = std::atomic>
template<class Rep , class Period >
FOLLY_ALWAYS_INLINE bool folly::SaturatingSemaphore< MayBlock, Atom >::try_wait_for ( const std::chrono::duration< Rep, Period > &  duration,
const WaitOptions opt = wait_options() 
)
inlinenoexcept

try_wait_for

Definition at line 183 of file SaturatingSemaphore.h.

Referenced by run_multi_poster_multi_waiter_test().

185  {
186  if (LIKELY(try_wait())) {
187  return true;
188  }
189  auto deadline = std::chrono::steady_clock::now() + duration;
190  return tryWaitSlow(deadline, opt);
191  }
std::chrono::steady_clock::time_point now()
#define LIKELY(x)
Definition: Likely.h:47
bool tryWaitSlow(const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt) noexcept
FOLLY_ALWAYS_INLINE bool try_wait() noexcept
template<bool MayBlock, template< typename > class Atom = std::atomic>
template<typename Clock , typename Duration >
FOLLY_ALWAYS_INLINE bool folly::SaturatingSemaphore< MayBlock, Atom >::try_wait_until ( const std::chrono::time_point< Clock, Duration > &  deadline,
const WaitOptions opt = wait_options() 
)
inlinenoexcept

try_wait_until

Definition at line 172 of file SaturatingSemaphore.h.

Referenced by run_basic_test(), run_multi_poster_multi_waiter_test(), TEST_F(), folly::UnboundedQueue< T, SingleProducer, SingleConsumer, MayBlock, LgSegmentSize, LgAlign, Atom >::Entry::tryWaitUntil(), and folly::SaturatingSemaphore< true, Atom >::wait().

174  {
175  if (LIKELY(try_wait())) {
176  return true;
177  }
178  return tryWaitSlow(deadline, opt);
179  }
#define LIKELY(x)
Definition: Likely.h:47
bool tryWaitSlow(const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt) noexcept
FOLLY_ALWAYS_INLINE bool try_wait() noexcept
template<bool MayBlock, template< typename > class Atom = std::atomic>
template<typename Clock , typename Duration >
bool folly::SaturatingSemaphore< MayBlock, Atom >::tryWaitSlow ( const std::chrono::time_point< Clock, Duration > &  deadline,
const WaitOptions opt 
)
privatenoexcept
template<bool MayBlock, template< typename > class Atom = std::atomic>
template<typename Clock , typename Duration >
FOLLY_NOINLINE bool folly::SaturatingSemaphore< MayBlock, Atom >::tryWaitSlow ( const std::chrono::time_point< Clock, Duration > &  deadline,
const WaitOptions opt 
)
noexcept

tryWaitSlow

Definition at line 280 of file SaturatingSemaphore.h.

References folly::detail::advance, folly::SaturatingSemaphore< MayBlock, Atom >::BLOCKED, folly::detail::MemoryIdler::futexWaitUntil(), max, folly::SaturatingSemaphore< MayBlock, Atom >::NOTREADY, folly::SaturatingSemaphore< MayBlock, Atom >::READY, folly::SaturatingSemaphore< MayBlock, Atom >::ready(), folly::detail::spin_pause_until(), folly::detail::spin_yield_until(), folly::detail::success, folly::detail::TIMEDOUT, and folly::detail::timeout.

282  {
283  switch (detail::spin_pause_until(deadline, opt, [=] { return ready(); })) {
285  return true;
287  return false;
289  break;
290  }
291 
292  if (!MayBlock) {
293  switch (detail::spin_yield_until(deadline, [=] { return ready(); })) {
295  return true;
297  return false;
299  break;
300  }
301  }
302 
303  auto before = state_.load(std::memory_order_relaxed);
304  while (before == NOTREADY &&
305  !state_.compare_exchange_strong(
306  before,
307  BLOCKED,
308  std::memory_order_relaxed,
309  std::memory_order_relaxed)) {
310  if (before == READY) {
311  // TODO: move the acquire to the compare_exchange failure load after C++17
312  std::atomic_thread_fence(std::memory_order_acquire);
313  return true;
314  }
315  }
316 
317  while (true) {
318  auto rv = detail::MemoryIdler::futexWaitUntil(state_, BLOCKED, deadline);
319  if (rv == detail::FutexResult::TIMEDOUT) {
320  assert(deadline != (std::chrono::time_point<Clock, Duration>::max()));
321  return false;
322  }
323 
324  if (ready()) {
325  return true;
326  }
327  }
328 }
FOLLY_ALWAYS_INLINE bool ready() const noexcept
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
detail::Futex< Atom > state_
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, template< typename > class Atom = std::atomic>
static FOLLY_ALWAYS_INLINE WaitOptions folly::SaturatingSemaphore< MayBlock, Atom >::wait_options ( )
inlinestatic

Member Data Documentation

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

Definition at line 121 of file SaturatingSemaphore.h.


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