Commit 8bfee85e authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Support for all clock types in Futex

Summary:
[Folly] Support for all clock types in `Futex`.

Only `system_clock` and `steady_clock` remain optimal as before, but at least let `Futex` work, even if non-optimally, for all clock types.

Reviewed By: nbronson

Differential Revision: D6673741

fbshipit-source-id: 0a0f778f61b71bea76e12b7fab478e33ce3bbaae
parent 0a6bc82e
......@@ -20,6 +20,7 @@
#include <cassert>
#include <chrono>
#include <limits>
#include <type_traits>
#include <boost/noncopyable.hpp>
......@@ -57,51 +58,25 @@ struct Futex : Atom<uint32_t>, boost::noncopyable {
return rv == FutexResult::AWOKEN;
}
/** Similar to futexWait but also accepts a timeout that gives the time until
* when the call can block (time is the absolute time i.e time since epoch).
* Allowed clock types: std::chrono::system_clock, std::chrono::steady_clock.
* Returns one of FutexResult values.
/** Similar to futexWait but also accepts a deadline until when the wait call
* may block.
*
* Optimal clock types: std::chrono::system_clock, std::chrono::steady_clock.
* NOTE: On some systems steady_clock is just an alias for system_clock,
* and is not actually steady.*/
* and is not actually steady.
*
* For any other clock type, now() will be invoked twice. */
template <class Clock, class Duration = typename Clock::duration>
FutexResult futexWaitUntil(
uint32_t expected,
const std::chrono::time_point<Clock, Duration>& absTime,
uint32_t waitMask = -1) {
using std::chrono::duration_cast;
using std::chrono::nanoseconds;
using std::chrono::seconds;
using std::chrono::steady_clock;
using std::chrono::system_clock;
using std::chrono::time_point;
static_assert(
(std::is_same<Clock, system_clock>::value ||
std::is_same<Clock, steady_clock>::value),
"futexWaitUntil only knows std::chrono::{system_clock,steady_clock}");
assert((std::is_same<Clock, system_clock>::value) || Clock::is_steady);
// We launder the clock type via a std::chrono::duration so that we
// can compile both the true and false branch. Tricky case is when
// steady_clock has a higher precision than system_clock (Xcode 6,
// for example), for which time_point<system_clock> construction
// refuses to do an implicit duration conversion. (duration is
// happy to implicitly convert its denominator causing overflow, but
// refuses conversion that might cause truncation.) We use explicit
// duration_cast to work around this. Truncation does not actually
// occur (unless Duration != Clock::duration) because the missing
// implicit conversion is in the untaken branch.
Duration absTimeDuration = absTime.time_since_epoch();
if (std::is_same<Clock, system_clock>::value) {
time_point<system_clock> absSystemTime(
duration_cast<system_clock::duration>(absTimeDuration));
return futexWaitImpl(expected, &absSystemTime, nullptr, waitMask);
} else {
time_point<steady_clock> absSteadyTime(
duration_cast<steady_clock::duration>(absTimeDuration));
return futexWaitImpl(expected, nullptr, &absSteadyTime, waitMask);
}
uint32_t expected,
std::chrono::time_point<Clock, Duration> const& deadline,
uint32_t waitMask = -1) {
using Target = typename std::conditional<
Clock::is_steady,
std::chrono::steady_clock,
std::chrono::system_clock>::type;
auto const converted = time_point_conv<Target>(deadline);
return futexWaitImpl(expected, converted, waitMask);
}
/** Wakens up to count waiters where (waitMask & wakeMask) !=
......@@ -116,6 +91,43 @@ struct Futex : Atom<uint32_t>, boost::noncopyable {
uint32_t wakeMask = -1);
private:
/** Optimal when TargetClock is the same type as Clock.
*
* Otherwise, both Clock::now() and TargetClock::now() must be invoked. */
template <typename TargetClock, typename Clock, typename Duration>
static typename TargetClock::time_point time_point_conv(
std::chrono::time_point<Clock, Duration> const& time) {
using std::chrono::duration_cast;
using TargetDuration = typename TargetClock::duration;
using TargetTimePoint = typename TargetClock::time_point;
if (std::is_same<Clock, TargetClock>::value) {
// in place of time_point_cast, which cannot compile without if-constexpr
auto const delta = time.time_since_epoch();
return TargetTimePoint(duration_cast<TargetDuration>(delta));
} else {
// different clocks with different epochs, so non-optimal case
auto const delta = time - Clock::now();
return TargetClock::now() + duration_cast<TargetDuration>(delta);
}
}
template <typename Deadline>
typename std::enable_if<Deadline::clock::is_steady, FutexResult>::type
futexWaitImpl(
uint32_t expected,
Deadline const& deadline,
uint32_t waitMask) {
return futexWaitImpl(expected, nullptr, &deadline, waitMask);
}
template <typename Deadline>
typename std::enable_if<!Deadline::clock::is_steady, FutexResult>::type
futexWaitImpl(
uint32_t expected,
Deadline const& deadline,
uint32_t waitMask) {
return futexWaitImpl(expected, &deadline, nullptr, waitMask);
}
/** Underlying implementation of futexWait and futexWaitUntil.
* At most one of absSystemTime and absSteadyTime should be non-null.
......
......@@ -24,6 +24,7 @@
#include <glog/logging.h>
#include <folly/Chrono.h>
#include <folly/portability/GTest.h>
#include <folly/portability/Time.h>
......@@ -31,6 +32,7 @@ using namespace folly::detail;
using namespace folly::test;
using namespace std;
using namespace std::chrono;
using folly::chrono::coarse_steady_clock;
typedef DeterministicSchedule DSched;
......@@ -117,6 +119,7 @@ template <template <typename> class Atom>
void run_wait_until_tests() {
liveClockWaitUntilTests<Atom, system_clock, system_clock::duration>();
liveClockWaitUntilTests<Atom, steady_clock, steady_clock::duration>();
liveClockWaitUntilTests<Atom, steady_clock, coarse_steady_clock::duration>();
typedef duration<int64_t, std::ratio<1, 10000000>> decimicroseconds;
liveClockWaitUntilTests<Atom, system_clock, decimicroseconds>();
......@@ -126,6 +129,7 @@ template <>
void run_wait_until_tests<DeterministicAtomic>() {
deterministicAtomicWaitUntilTests<system_clock>();
deterministicAtomicWaitUntilTests<steady_clock>();
deterministicAtomicWaitUntilTests<coarse_steady_clock>();
}
uint64_t diff(uint64_t a, uint64_t b) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment