1 | /* |
2 | * Copyright 2013-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #pragma once |
18 | |
19 | #include <folly/detail/Futex.h> |
20 | #include <folly/synchronization/ParkingLot.h> |
21 | |
22 | namespace folly { |
23 | namespace detail { |
24 | |
25 | /** Optimal when TargetClock is the same type as Clock. |
26 | * |
27 | * Otherwise, both Clock::now() and TargetClock::now() must be invoked. */ |
28 | template <typename TargetClock, typename Clock, typename Duration> |
29 | typename TargetClock::time_point time_point_conv( |
30 | std::chrono::time_point<Clock, Duration> const& time) { |
31 | using std::chrono::duration_cast; |
32 | using TimePoint = std::chrono::time_point<Clock, Duration>; |
33 | using TargetDuration = typename TargetClock::duration; |
34 | using TargetTimePoint = typename TargetClock::time_point; |
35 | if (time == TimePoint::max()) { |
36 | return TargetTimePoint::max(); |
37 | } else if (std::is_same<Clock, TargetClock>::value) { |
38 | // in place of time_point_cast, which cannot compile without if-constexpr |
39 | auto const delta = time.time_since_epoch(); |
40 | return TargetTimePoint(duration_cast<TargetDuration>(delta)); |
41 | } else { |
42 | // different clocks with different epochs, so non-optimal case |
43 | auto const delta = time - Clock::now(); |
44 | return TargetClock::now() + duration_cast<TargetDuration>(delta); |
45 | } |
46 | } |
47 | |
48 | /** |
49 | * Available overloads, with definitions elsewhere |
50 | * |
51 | * These functions are treated as ADL-extension points, the templates above |
52 | * call these functions without them having being pre-declared. This works |
53 | * because ADL lookup finds the definitions of these functions when you pass |
54 | * the relevant arguments |
55 | */ |
56 | int futexWakeImpl( |
57 | const Futex<std::atomic>* futex, |
58 | int count, |
59 | uint32_t wakeMask); |
60 | FutexResult futexWaitImpl( |
61 | const Futex<std::atomic>* 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 | int futexWakeImpl( |
68 | const Futex<EmulatedFutexAtomic>* futex, |
69 | int count, |
70 | uint32_t wakeMask); |
71 | FutexResult futexWaitImpl( |
72 | const Futex<EmulatedFutexAtomic>* futex, |
73 | uint32_t expected, |
74 | std::chrono::system_clock::time_point const* absSystemTime, |
75 | std::chrono::steady_clock::time_point const* absSteadyTime, |
76 | uint32_t waitMask); |
77 | |
78 | template <typename Futex, typename Deadline> |
79 | typename std::enable_if<Deadline::clock::is_steady, FutexResult>::type |
80 | futexWaitImpl( |
81 | Futex* futex, |
82 | uint32_t expected, |
83 | Deadline const& deadline, |
84 | uint32_t waitMask) { |
85 | return futexWaitImpl(futex, expected, nullptr, &deadline, waitMask); |
86 | } |
87 | |
88 | template <typename Futex, typename Deadline> |
89 | typename std::enable_if<!Deadline::clock::is_steady, FutexResult>::type |
90 | futexWaitImpl( |
91 | Futex* futex, |
92 | uint32_t expected, |
93 | Deadline const& deadline, |
94 | uint32_t waitMask) { |
95 | return futexWaitImpl(futex, expected, &deadline, nullptr, waitMask); |
96 | } |
97 | |
98 | template <typename Futex> |
99 | FutexResult |
100 | futexWait(const Futex* futex, uint32_t expected, uint32_t waitMask) { |
101 | auto rv = futexWaitImpl(futex, expected, nullptr, nullptr, waitMask); |
102 | assert(rv != FutexResult::TIMEDOUT); |
103 | return rv; |
104 | } |
105 | |
106 | template <typename Futex> |
107 | int futexWake(const Futex* futex, int count, uint32_t wakeMask) { |
108 | return futexWakeImpl(futex, count, wakeMask); |
109 | } |
110 | |
111 | template <typename Futex, class Clock, class Duration> |
112 | FutexResult futexWaitUntil( |
113 | const Futex* futex, |
114 | uint32_t expected, |
115 | std::chrono::time_point<Clock, Duration> const& deadline, |
116 | uint32_t waitMask) { |
117 | using Target = typename std::conditional< |
118 | Clock::is_steady, |
119 | std::chrono::steady_clock, |
120 | std::chrono::system_clock>::type; |
121 | auto const converted = time_point_conv<Target>(deadline); |
122 | return converted == Target::time_point::max() |
123 | ? futexWaitImpl(futex, expected, nullptr, nullptr, waitMask) |
124 | : futexWaitImpl(futex, expected, converted, waitMask); |
125 | } |
126 | |
127 | } // namespace detail |
128 | } // namespace folly |
129 | |