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 @@ ...@@ -20,6 +20,7 @@
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <limits> #include <limits>
#include <type_traits>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
...@@ -57,51 +58,25 @@ struct Futex : Atom<uint32_t>, boost::noncopyable { ...@@ -57,51 +58,25 @@ struct Futex : Atom<uint32_t>, boost::noncopyable {
return rv == FutexResult::AWOKEN; return rv == FutexResult::AWOKEN;
} }
/** Similar to futexWait but also accepts a timeout that gives the time until /** Similar to futexWait but also accepts a deadline until when the wait call
* when the call can block (time is the absolute time i.e time since epoch). * may block.
* Allowed clock types: std::chrono::system_clock, std::chrono::steady_clock.
* Returns one of FutexResult values.
* *
* Optimal clock types: std::chrono::system_clock, std::chrono::steady_clock.
* NOTE: On some systems steady_clock is just an alias for system_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> template <class Clock, class Duration = typename Clock::duration>
FutexResult futexWaitUntil( FutexResult futexWaitUntil(
uint32_t expected, uint32_t expected,
const std::chrono::time_point<Clock, Duration>& absTime, std::chrono::time_point<Clock, Duration> const& deadline,
uint32_t waitMask = -1) { uint32_t waitMask = -1) {
using std::chrono::duration_cast; using Target = typename std::conditional<
using std::chrono::nanoseconds; Clock::is_steady,
using std::chrono::seconds; std::chrono::steady_clock,
using std::chrono::steady_clock; std::chrono::system_clock>::type;
using std::chrono::system_clock; auto const converted = time_point_conv<Target>(deadline);
using std::chrono::time_point; return futexWaitImpl(expected, converted, waitMask);
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);
}
} }
/** Wakens up to count waiters where (waitMask & wakeMask) != /** Wakens up to count waiters where (waitMask & wakeMask) !=
...@@ -116,6 +91,43 @@ struct Futex : Atom<uint32_t>, boost::noncopyable { ...@@ -116,6 +91,43 @@ struct Futex : Atom<uint32_t>, boost::noncopyable {
uint32_t wakeMask = -1); uint32_t wakeMask = -1);
private: 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. /** Underlying implementation of futexWait and futexWaitUntil.
* At most one of absSystemTime and absSteadyTime should be non-null. * At most one of absSystemTime and absSteadyTime should be non-null.
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <glog/logging.h> #include <glog/logging.h>
#include <folly/Chrono.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
#include <folly/portability/Time.h> #include <folly/portability/Time.h>
...@@ -31,6 +32,7 @@ using namespace folly::detail; ...@@ -31,6 +32,7 @@ using namespace folly::detail;
using namespace folly::test; using namespace folly::test;
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
using folly::chrono::coarse_steady_clock;
typedef DeterministicSchedule DSched; typedef DeterministicSchedule DSched;
...@@ -117,6 +119,7 @@ template <template <typename> class Atom> ...@@ -117,6 +119,7 @@ template <template <typename> class Atom>
void run_wait_until_tests() { void run_wait_until_tests() {
liveClockWaitUntilTests<Atom, system_clock, system_clock::duration>(); liveClockWaitUntilTests<Atom, system_clock, system_clock::duration>();
liveClockWaitUntilTests<Atom, steady_clock, steady_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; typedef duration<int64_t, std::ratio<1, 10000000>> decimicroseconds;
liveClockWaitUntilTests<Atom, system_clock, decimicroseconds>(); liveClockWaitUntilTests<Atom, system_clock, decimicroseconds>();
...@@ -126,6 +129,7 @@ template <> ...@@ -126,6 +129,7 @@ template <>
void run_wait_until_tests<DeterministicAtomic>() { void run_wait_until_tests<DeterministicAtomic>() {
deterministicAtomicWaitUntilTests<system_clock>(); deterministicAtomicWaitUntilTests<system_clock>();
deterministicAtomicWaitUntilTests<steady_clock>(); deterministicAtomicWaitUntilTests<steady_clock>();
deterministicAtomicWaitUntilTests<coarse_steady_clock>();
} }
uint64_t diff(uint64_t a, uint64_t b) { 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