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