proxygen
|
#include <TurnSequencer.h>
Public Types | |
enum | TryWaitResult { TryWaitResult::SUCCESS, TryWaitResult::PAST, TryWaitResult::TIMEDOUT } |
Public Member Functions | |
TurnSequencer (const uint32_t firstTurn=0) noexcept | |
bool | isTurn (const uint32_t turn) const noexcept |
Returns true iff a call to waitForTurn(turn, ...) won't block. More... | |
void | waitForTurn (const uint32_t turn, Atom< uint32_t > &spinCutoff, const bool updateSpinCutoff) noexcept |
template<class Clock = std::chrono::steady_clock, class Duration = typename Clock::duration> | |
TryWaitResult | tryWaitForTurn (const uint32_t turn, Atom< uint32_t > &spinCutoff, const bool updateSpinCutoff, const std::chrono::time_point< Clock, Duration > *absTime=nullptr) noexcept |
void | completeTurn (const uint32_t turn) noexcept |
Unblocks a thread running waitForTurn(turn + 1) More... | |
uint8_t | uncompletedTurnLSB () const noexcept |
Private Types | |
enum | : uint32_t { kTurnShift = 6, kWaitersMask = (1 << kTurnShift) - 1, kMinSpins = 20, kMaxSpins = 2000 } |
Private Member Functions | |
uint32_t | futexChannel (uint32_t turn) const noexcept |
uint32_t | decodeCurrentSturn (uint32_t state) const noexcept |
uint32_t | decodeMaxWaitersDelta (uint32_t state) const noexcept |
uint32_t | encode (uint32_t currentSturn, uint32_t maxWaiterD) const noexcept |
Private Attributes | |
Futex< Atom > | state_ |
A TurnSequencer allows threads to order their execution according to a monotonically increasing (with wraparound) "turn" value. The two operations provided are to wait for turn T, and to move to the next turn. Every thread that is waiting for T must have arrived before that turn is marked completed (for MPMCQueue only one thread waits for any particular turn, so this is trivially true).
TurnSequencer's state_ holds 26 bits of the current turn (shifted left by 6), along with a 6 bit saturating value that records the maximum waiter minus the current turn. Wraparound of the turn space is expected and handled. This allows us to atomically adjust the number of outstanding waiters when we perform a FUTEX_WAKE operation. Compare this strategy to sem_t's separate num_waiters field, which isn't decremented until after the waiting thread gets scheduled, during which time more enqueues might have occurred and made pointless FUTEX_WAKE calls.
TurnSequencer uses futex() directly. It is optimized for the case that the highest awaited turn is 32 or less higher than the current turn. We use the FUTEX_WAIT_BITSET variant, which lets us embed 32 separate wakeup channels in a single futex. See http://locklessinc.com/articles/futex_cheat_sheet for a description.
We only need to keep exact track of the delta between the current turn and the maximum waiter for the 32 turns that follow the current one, because waiters at turn t+32 will be awoken at turn t. At that point they can then adjust the delta using the higher base. Since we need to encode waiter deltas of 0 to 32 inclusive, we use 6 bits. We actually store waiter deltas up to 63, since that might reduce the number of CAS operations a tiny bit.
To avoid some futex() calls entirely, TurnSequencer uses an adaptive spin cutoff before waiting. The overheads (and convergence rate) of separately tracking the spin cutoff for each TurnSequencer would be prohibitive, so the actual storage is passed in as a parameter and updated atomically. This also lets the caller use different adaptive cutoffs for different operations (read versus write, for example). To avoid contention, the spin cutoff is only updated when requested by the caller.
Definition at line 72 of file TurnSequencer.h.
|
private |
Definition at line 222 of file TurnSequencer.h.
|
strong |
Enumerator | |
---|---|
SUCCESS | |
PAST | |
TIMEDOUT |
Definition at line 82 of file TurnSequencer.h.
|
inlineexplicitnoexcept |
Definition at line 73 of file TurnSequencer.h.
|
inlinenoexcept |
Unblocks a thread running waitForTurn(turn + 1)
Definition at line 195 of file TurnSequencer.h.
Referenced by folly::detail::SingleElementQueue< T, Atom >::dequeueImpl(), and folly::detail::SingleElementQueue< T, Atom >::enqueueImpl().
|
inlineprivatenoexcept |
Definition at line 252 of file TurnSequencer.h.
Referenced by folly::detail::TurnSequencer< std::atomic >::isTurn(), and folly::detail::TurnSequencer< std::atomic >::tryWaitForTurn().
|
inlineprivatenoexcept |
Definition at line 256 of file TurnSequencer.h.
Referenced by folly::detail::TurnSequencer< std::atomic >::completeTurn(), and folly::detail::TurnSequencer< std::atomic >::tryWaitForTurn().
|
inlineprivatenoexcept |
Definition at line 260 of file TurnSequencer.h.
Referenced by folly::detail::TurnSequencer< std::atomic >::completeTurn(), and folly::detail::TurnSequencer< std::atomic >::tryWaitForTurn().
|
inlineprivatenoexcept |
Returns the bitmask to pass futexWait or futexWake when communicating about the specified turn
Definition at line 248 of file TurnSequencer.h.
Referenced by folly::detail::TurnSequencer< std::atomic >::completeTurn(), and folly::detail::TurnSequencer< std::atomic >::tryWaitForTurn().
|
inlinenoexcept |
Returns true iff a call to waitForTurn(turn, ...) won't block.
Definition at line 77 of file TurnSequencer.h.
|
inlinenoexcept |
Blocks the current thread until turn has arrived. If updateSpinCutoff is true then this will spin for up to kMaxSpins tries before blocking and will adjust spinCutoff based on the results, otherwise it will spin for at most spinCutoff spins. Returns SUCCESS if the wait succeeded, PAST if the turn is in the past or TIMEDOUT if the absTime time value is not nullptr and is reached before the turn arrives
Definition at line 109 of file TurnSequencer.h.
Referenced by folly::detail::TurnSequencer< std::atomic >::waitForTurn().
|
inlinenoexcept |
Returns the least-most significant byte of the current uncompleted turn. The full 32 bit turn cannot be recovered.
Definition at line 217 of file TurnSequencer.h.
|
inlinenoexcept |
See tryWaitForTurn Requires that turn
is not a turn in the past.
Definition at line 86 of file TurnSequencer.h.
Referenced by folly::detail::SingleElementQueue< T, Atom >::dequeueImpl(), and folly::detail::SingleElementQueue< T, Atom >::enqueueImpl().
|
private |
This holds both the current turn, and the highest waiting turn, stored as (current_turn << 6) | min(63, max(waited_turn - current_turn))
Definition at line 244 of file TurnSequencer.h.
Referenced by folly::detail::TurnSequencer< std::atomic >::completeTurn(), folly::detail::TurnSequencer< std::atomic >::isTurn(), folly::detail::TurnSequencer< std::atomic >::tryWaitForTurn(), and folly::detail::TurnSequencer< std::atomic >::uncompletedTurnLSB().