From: Yedidya Feldblum Date: Wed, 10 Jan 2018 06:39:11 +0000 (-0800) Subject: Support for all clock types in Futex X-Git-Tag: v2018.01.15.00~25 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=8bfee85eb0e54a346a8c2fe1ac8b474303b754b0;ds=sidebyside 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 --- diff --git a/folly/detail/Futex.h b/folly/detail/Futex.h index a2e35020..1e72b15c 100644 --- a/folly/detail/Futex.h +++ b/folly/detail/Futex.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -57,51 +58,25 @@ struct Futex : Atom, 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 FutexResult futexWaitUntil( - uint32_t expected, - const std::chrono::time_point& 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::value || - std::is_same::value), - "futexWaitUntil only knows std::chrono::{system_clock,steady_clock}"); - assert((std::is_same::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 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::value) { - time_point absSystemTime( - duration_cast(absTimeDuration)); - return futexWaitImpl(expected, &absSystemTime, nullptr, waitMask); - } else { - time_point absSteadyTime( - duration_cast(absTimeDuration)); - return futexWaitImpl(expected, nullptr, &absSteadyTime, waitMask); - } + uint32_t expected, + std::chrono::time_point 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(deadline); + return futexWaitImpl(expected, converted, waitMask); } /** Wakens up to count waiters where (waitMask & wakeMask) != @@ -116,6 +91,43 @@ struct Futex : Atom, 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 + static typename TargetClock::time_point time_point_conv( + std::chrono::time_point const& time) { + using std::chrono::duration_cast; + using TargetDuration = typename TargetClock::duration; + using TargetTimePoint = typename TargetClock::time_point; + if (std::is_same::value) { + // in place of time_point_cast, which cannot compile without if-constexpr + auto const delta = time.time_since_epoch(); + return TargetTimePoint(duration_cast(delta)); + } else { + // different clocks with different epochs, so non-optimal case + auto const delta = time - Clock::now(); + return TargetClock::now() + duration_cast(delta); + } + } + + template + typename std::enable_if::type + futexWaitImpl( + uint32_t expected, + Deadline const& deadline, + uint32_t waitMask) { + return futexWaitImpl(expected, nullptr, &deadline, waitMask); + } + + template + typename std::enable_if::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. diff --git a/folly/test/FutexTest.cpp b/folly/test/FutexTest.cpp index 71793a97..464c8524 100644 --- a/folly/test/FutexTest.cpp +++ b/folly/test/FutexTest.cpp @@ -24,6 +24,7 @@ #include +#include #include #include @@ -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