1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 
6 #pragma once
7 
8 #include <folly/detail/Futex.h>
9 #include <folly/synchronization/ParkingLot.h>
10 
11 namespace folly {
12 namespace detail {
13 
14 /** Optimal when TargetClock is the same type as Clock.
15  *
16  *  Otherwise, both Clock::now() and TargetClock::now() must be invoked. */
17 template <typename TargetClock, typename Clock, typename Duration>
time_point_conv(std::chrono::time_point<Clock,Duration> const & time)18 typename TargetClock::time_point time_point_conv(
19     std::chrono::time_point<Clock, Duration> const& time) {
20   using std::chrono::duration_cast;
21   using TimePoint = std::chrono::time_point<Clock, Duration>;
22   using TargetDuration = typename TargetClock::duration;
23   using TargetTimePoint = typename TargetClock::time_point;
24   if (time == TimePoint::max()) {
25     return TargetTimePoint::max();
26   } else if (std::is_same<Clock, TargetClock>::value) {
27     // in place of time_point_cast, which cannot compile without if-constexpr
28     auto const delta = time.time_since_epoch();
29     return TargetTimePoint(duration_cast<TargetDuration>(delta));
30   } else {
31     // different clocks with different epochs, so non-optimal case
32     auto const delta = time - Clock::now();
33     return TargetClock::now() + duration_cast<TargetDuration>(delta);
34   }
35 }
36 
37 /**
38  * Available overloads, with definitions elsewhere
39  *
40  * These functions are treated as ADL-extension points, the templates above
41  * call these functions without them having being pre-declared.  This works
42  * because ADL lookup finds the definitions of these functions when you pass
43  * the relevant arguments
44  */
45 int futexWakeImpl(
46     const Futex<std::atomic>* futex,
47     int count,
48     uint32_t wakeMask);
49 FutexResult futexWaitImpl(
50     const Futex<std::atomic>* futex,
51     uint32_t expected,
52     std::chrono::system_clock::time_point const* absSystemTime,
53     std::chrono::steady_clock::time_point const* absSteadyTime,
54     uint32_t waitMask);
55 
56 int futexWakeImpl(
57     const Futex<EmulatedFutexAtomic>* futex,
58     int count,
59     uint32_t wakeMask);
60 FutexResult futexWaitImpl(
61     const Futex<EmulatedFutexAtomic>* futex,
62     uint32_t expected,
63     std::chrono::system_clock::time_point const* absSystemTime,
64     std::chrono::steady_clock::time_point const* absSteadyTime,
65     uint32_t waitMask);
66 
67 template <typename Futex, typename Deadline>
68 typename std::enable_if<Deadline::clock::is_steady, FutexResult>::type
futexWaitImpl(Futex * futex,uint32_t expected,Deadline const & deadline,uint32_t waitMask)69 futexWaitImpl(
70     Futex* futex,
71     uint32_t expected,
72     Deadline const& deadline,
73     uint32_t waitMask) {
74   return futexWaitImpl(futex, expected, nullptr, &deadline, waitMask);
75 }
76 
77 template <typename Futex, typename Deadline>
78 typename std::enable_if<!Deadline::clock::is_steady, FutexResult>::type
futexWaitImpl(Futex * futex,uint32_t expected,Deadline const & deadline,uint32_t waitMask)79 futexWaitImpl(
80     Futex* futex,
81     uint32_t expected,
82     Deadline const& deadline,
83     uint32_t waitMask) {
84   return futexWaitImpl(futex, expected, &deadline, nullptr, waitMask);
85 }
86 
87 template <typename Futex>
88 FutexResult
futexWait(const Futex * futex,uint32_t expected,uint32_t waitMask)89 futexWait(const Futex* futex, uint32_t expected, uint32_t waitMask) {
90   auto rv = futexWaitImpl(futex, expected, nullptr, nullptr, waitMask);
91   assert(rv != FutexResult::TIMEDOUT);
92   return rv;
93 }
94 
95 template <typename Futex>
futexWake(const Futex * futex,int count,uint32_t wakeMask)96 int futexWake(const Futex* futex, int count, uint32_t wakeMask) {
97   return futexWakeImpl(futex, count, wakeMask);
98 }
99 
100 template <typename Futex, class Clock, class Duration>
futexWaitUntil(const Futex * futex,uint32_t expected,std::chrono::time_point<Clock,Duration> const & deadline,uint32_t waitMask)101 FutexResult futexWaitUntil(
102     const Futex* futex,
103     uint32_t expected,
104     std::chrono::time_point<Clock, Duration> const& deadline,
105     uint32_t waitMask) {
106   using Target = typename std::conditional<
107       Clock::is_steady,
108       std::chrono::steady_clock,
109       std::chrono::system_clock>::type;
110   auto const converted = time_point_conv<Target>(deadline);
111   return converted == Target::time_point::max()
112       ? futexWaitImpl(futex, expected, nullptr, nullptr, waitMask)
113       : futexWaitImpl(futex, expected, converted, waitMask);
114 }
115 
116 } // namespace detail
117 } // namespace folly
118