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
22namespace folly {
23namespace detail {
24
25/** Optimal when TargetClock is the same type as Clock.
26 *
27 * Otherwise, both Clock::now() and TargetClock::now() must be invoked. */
28template <typename TargetClock, typename Clock, typename Duration>
29typename 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 */
56int futexWakeImpl(
57 const Futex<std::atomic>* futex,
58 int count,
59 uint32_t wakeMask);
60FutexResult 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
67int futexWakeImpl(
68 const Futex<EmulatedFutexAtomic>* futex,
69 int count,
70 uint32_t wakeMask);
71FutexResult 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
78template <typename Futex, typename Deadline>
79typename std::enable_if<Deadline::clock::is_steady, FutexResult>::type
80futexWaitImpl(
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
88template <typename Futex, typename Deadline>
89typename std::enable_if<!Deadline::clock::is_steady, FutexResult>::type
90futexWaitImpl(
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
98template <typename Futex>
99FutexResult
100futexWait(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
106template <typename Futex>
107int futexWake(const Futex* futex, int count, uint32_t wakeMask) {
108 return futexWakeImpl(futex, count, wakeMask);
109}
110
111template <typename Futex, class Clock, class Duration>
112FutexResult 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