/// Selects a timeout pseudo-randomly chosen to be between
/// idleTimeout and idleTimeout * (1 + timeoutVariationFraction), to
/// smooth out the behavior in a bursty system
- template <typename Clock = std::chrono::steady_clock>
- static typename Clock::duration getVariationTimeout(
- typename Clock::duration idleTimeout
- = defaultIdleTimeout.load(std::memory_order_acquire),
+ template <typename IdleTime = std::chrono::steady_clock::duration>
+ static IdleTime getVariationTimeout(
+ IdleTime const& idleTimeout =
+ defaultIdleTimeout.load(std::memory_order_acquire),
float timeoutVariationFrac = 0.5) {
- if (idleTimeout.count() > 0 && timeoutVariationFrac > 0) {
- // hash the pthread_t and the time to get the adjustment.
- // Standard hash func isn't very good, so bit mix the result
- auto pr = std::make_pair(getCurrentThreadID(),
- Clock::now().time_since_epoch().count());
- std::hash<decltype(pr)> hash_fn;
- uint64_t h = folly::hash::twang_mix64(hash_fn(pr));
-
- // multiplying the duration by a floating point doesn't work, grr..
- auto extraFrac =
- timeoutVariationFrac / std::numeric_limits<uint64_t>::max() * h;
- auto tics = uint64_t(idleTimeout.count() * (1 + extraFrac));
- idleTimeout = typename Clock::duration(tics);
+ if (idleTimeout <= IdleTime::zero() || timeoutVariationFrac <= 0) {
+ return idleTimeout;
}
- return idleTimeout;
+ // hash the pthread_t and the time to get the adjustment
+ // Standard hash func isn't very good, so bit mix the result
+ uint64_t h = folly::hash::twang_mix64(folly::hash::hash_combine(
+ getCurrentThreadID(),
+ std::chrono::system_clock::now().time_since_epoch().count()));
+
+ // multiplying the duration by a floating point doesn't work, grr
+ auto extraFrac =
+ timeoutVariationFrac / std::numeric_limits<uint64_t>::max() * h;
+ auto tics = uint64_t(idleTimeout.count() * (1 + extraFrac));
+ return IdleTime(tics);
}
/// Equivalent to fut.futexWait(expected, waitMask), but calls
/// flushLocalMallocCaches() and unmapUnusedStack(stackToRetain)
- /// after idleTimeout has passed (if it has passed). Internally uses
- /// fut.futexWait and fut.futexWaitUntil. Like futexWait, returns
- /// false if interrupted with a signal. The actual timeout will be
+ /// after idleTimeout has passed (if it has passed). Internally uses
+ /// fut.futexWait and fut.futexWaitUntil. The actual timeout will be
/// pseudo-randomly chosen to be between idleTimeout and idleTimeout *
/// (1 + timeoutVariationFraction), to smooth out the behavior in a
- /// system with bursty requests. The default is to wait up to 50%
- /// extra, so on average 25% extra
+ /// system with bursty requests. The default is to wait up to 50%
+ /// extra, so on average 25% extra.
template <
template <typename> class Atom,
- typename Clock = std::chrono::steady_clock>
+ typename IdleTime = std::chrono::steady_clock::duration>
static FutexResult futexWait(
Futex<Atom>& fut,
uint32_t expected,
uint32_t waitMask = -1,
- typename Clock::duration idleTimeout =
+ IdleTime const& idleTimeout =
+ defaultIdleTimeout.load(std::memory_order_acquire),
+ size_t stackToRetain = kDefaultStackToRetain,
+ float timeoutVariationFrac = 0.5) {
+ FutexResult pre;
+ if (futexWaitPreIdle(
+ pre,
+ fut,
+ expected,
+ std::chrono::steady_clock::time_point::max(),
+ waitMask,
+ idleTimeout,
+ stackToRetain,
+ timeoutVariationFrac)) {
+ return pre;
+ }
+ return fut.futexWait(expected, waitMask);
+ }
+
+ /// Equivalent to fut.futexWaitUntil(expected, deadline, waitMask), but
+ /// calls flushLocalMallocCaches() and unmapUnusedStack(stackToRetain)
+ /// after idleTimeout has passed (if it has passed). Internally uses
+ /// fut.futexWaitUntil. The actual timeout will be pseudo-randomly
+ /// chosen to be between idleTimeout and idleTimeout *
+ /// (1 + timeoutVariationFraction), to smooth out the behavior in a
+ /// system with bursty requests. The default is to wait up to 50%
+ /// extra, so on average 25% extra.
+ template <
+ template <typename> class Atom,
+ typename Deadline,
+ typename IdleTime = std::chrono::steady_clock::duration>
+ static FutexResult futexWaitUntil(
+ Futex<Atom>& 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) {
- if (idleTimeout == Clock::duration::max()) {
- // no need to use futexWaitUntil if no timeout is possible
- return fut.futexWait(expected, waitMask);
+ FutexResult pre;
+ if (futexWaitPreIdle(
+ pre,
+ fut,
+ expected,
+ deadline,
+ waitMask,
+ idleTimeout,
+ stackToRetain,
+ timeoutVariationFrac)) {
+ return pre;
}
+ return fut.futexWaitUntil(expected, deadline, waitMask);
+ }
- idleTimeout = getVariationTimeout(idleTimeout, timeoutVariationFrac);
- if (idleTimeout.count() > 0) {
- while (true) {
- auto rv = fut.futexWaitUntil(
- expected, Clock::now() + idleTimeout, waitMask);
- if (rv == FutexResult::TIMEDOUT) {
- // timeout is over
- break;
+ private:
+ template <
+ template <typename> class Atom,
+ typename Deadline,
+ typename IdleTime>
+ static bool futexWaitPreIdle(
+ FutexResult& _ret,
+ Futex<Atom>& fut,
+ uint32_t expected,
+ Deadline const& deadline,
+ uint32_t waitMask,
+ IdleTime idleTimeout,
+ size_t stackToRetain,
+ float timeoutVariationFrac) {
+ // idleTimeout < 0 means no flush behavior
+ if (idleTimeout < IdleTime::zero()) {
+ return false;
+ }
+
+ // idleTimeout == 0 means flush immediately, without variation
+ // idleTimeout > 0 means flush after delay, with variation
+ if (idleTimeout > IdleTime::zero()) {
+ idleTimeout = std::max(
+ IdleTime::zero(),
+ getVariationTimeout(idleTimeout, timeoutVariationFrac));
+ }
+ if (idleTimeout > IdleTime::zero()) {
+ auto idleDeadline = Deadline::clock::now() + idleTimeout;
+ if (idleDeadline < deadline) {
+ while (true) {
+ auto rv = fut.futexWaitUntil(expected, idleDeadline, waitMask);
+ if (rv == FutexResult::TIMEDOUT) {
+ break;
+ }
+ // finished before timeout hit, no flush
+ _ret = rv;
+ return true;
}
- // finished before timeout hit, no flush
- assert(rv == FutexResult::VALUE_CHANGED || rv == FutexResult::AWOKEN ||
- rv == FutexResult::INTERRUPTED);
- return rv;
}
}
- // flush, then wait with no timeout
+ // flush, then wait
flushLocalMallocCaches();
unmapUnusedStack(stackToRetain);
- return fut.futexWait(expected, waitMask);
+ return false;
}
};