1 | /* |
2 | * Copyright 2004-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 | #pragma once |
17 | |
18 | #include <folly/detail/Futex.h> |
19 | #include <folly/synchronization/ParkingLot.h> |
20 | |
21 | #include <condition_variable> |
22 | #include <cstdint> |
23 | |
24 | namespace folly { |
25 | namespace detail { |
26 | namespace atomic_notification { |
27 | /** |
28 | * We use Futex<std::atomic> as the alias that has the lowest performance |
29 | * overhead with respect to atomic notifications. Assert that |
30 | * atomic_uint_fast_wait_t is the same as Futex<std::atomic> |
31 | */ |
32 | static_assert(std::is_same<atomic_uint_fast_wait_t, Futex<std::atomic>>{}, "" ); |
33 | |
34 | /** |
35 | * Implementation and specializations for the atomic_wait() family of |
36 | * functions |
37 | */ |
38 | inline std::cv_status toCvStatus(FutexResult result) { |
39 | return (result == FutexResult::TIMEDOUT) ? std::cv_status::timeout |
40 | : std::cv_status::no_timeout; |
41 | } |
42 | inline std::cv_status toCvStatus(ParkResult result) { |
43 | return (result == ParkResult::Timeout) ? std::cv_status::timeout |
44 | : std::cv_status::no_timeout; |
45 | } |
46 | |
47 | // ParkingLot instantiation for futex management |
48 | extern ParkingLot<std::uint32_t> parkingLot; |
49 | |
50 | template <template <typename...> class Atom, typename... Args> |
51 | void atomic_wait_impl( |
52 | const Atom<std::uint32_t, Args...>* atomic, |
53 | std::uint32_t expected) { |
54 | futexWait(atomic, expected); |
55 | return; |
56 | } |
57 | |
58 | template <template <typename...> class Atom, typename Integer, typename... Args> |
59 | void atomic_wait_impl(const Atom<Integer, Args...>* atomic, Integer expected) { |
60 | static_assert(!std::is_same<Integer, std::uint32_t>{}, "" ); |
61 | parkingLot.park( |
62 | atomic, -1, [&] { return atomic->load() == expected; }, [] {}); |
63 | } |
64 | |
65 | template < |
66 | template <typename...> class Atom, |
67 | typename... Args, |
68 | typename Clock, |
69 | typename Duration> |
70 | std::cv_status atomic_wait_until_impl( |
71 | const Atom<std::uint32_t, Args...>* atomic, |
72 | std::uint32_t expected, |
73 | const std::chrono::time_point<Clock, Duration>& deadline) { |
74 | return toCvStatus(futexWaitUntil(atomic, expected, deadline)); |
75 | } |
76 | |
77 | template < |
78 | template <typename...> class Atom, |
79 | typename Integer, |
80 | typename... Args, |
81 | typename Clock, |
82 | typename Duration> |
83 | std::cv_status atomic_wait_until_impl( |
84 | const Atom<Integer, Args...>* atomic, |
85 | Integer expected, |
86 | const std::chrono::time_point<Clock, Duration>& deadline) { |
87 | static_assert(!std::is_same<Integer, std::uint32_t>{}, "" ); |
88 | return toCvStatus(parkingLot.park_until( |
89 | atomic, -1, [&] { return atomic->load() == expected; }, [] {}, deadline)); |
90 | } |
91 | |
92 | template <template <typename...> class Atom, typename... Args> |
93 | void atomic_notify_one_impl(const Atom<std::uint32_t, Args...>* atomic) { |
94 | futexWake(atomic, 1); |
95 | return; |
96 | } |
97 | |
98 | template <template <typename...> class Atom, typename Integer, typename... Args> |
99 | void atomic_notify_one_impl(const Atom<Integer, Args...>* atomic) { |
100 | static_assert(!std::is_same<Integer, std::uint32_t>{}, "" ); |
101 | parkingLot.unpark(atomic, [&](const auto& data) { |
102 | FOLLY_SAFE_DCHECK(data == std::numeric_limits<std::uint32_t>::max(), "" ); |
103 | return UnparkControl::RemoveBreak; |
104 | }); |
105 | } |
106 | |
107 | template <template <typename...> class Atom, typename Integer, typename... Args> |
108 | void atomic_notify_all_impl(const Atom<std::uint32_t, Args...>* atomic) { |
109 | futexWake(atomic); |
110 | return; |
111 | } |
112 | |
113 | template <template <typename...> class Atom, typename Integer, typename... Args> |
114 | void atomic_notify_all_impl(const Atom<Integer, Args...>* atomic) { |
115 | static_assert(!std::is_same<Integer, std::uint32_t>{}, "" ); |
116 | parkingLot.unpark(atomic, [&](const auto& data) { |
117 | FOLLY_SAFE_DCHECK(data == std::numeric_limits<std::uint32_t>::max(), "" ); |
118 | return UnparkControl::RemoveContinue; |
119 | }); |
120 | } |
121 | } // namespace atomic_notification |
122 | } // namespace detail |
123 | |
124 | template <typename Integer> |
125 | void atomic_wait(const std::atomic<Integer>* atomic, Integer expected) { |
126 | detail::atomic_notification::atomic_wait_impl(atomic, expected); |
127 | } |
128 | |
129 | template <typename Integer, typename Clock, typename Duration> |
130 | std::cv_status atomic_wait_until( |
131 | const std::atomic<Integer>* atomic, |
132 | Integer expected, |
133 | const std::chrono::time_point<Clock, Duration>& deadline) { |
134 | return detail::atomic_notification::atomic_wait_until_impl( |
135 | atomic, expected, deadline); |
136 | } |
137 | |
138 | template <typename Integer> |
139 | void atomic_notify_one(const std::atomic<Integer>* atomic) { |
140 | detail::atomic_notification::atomic_notify_one_impl(atomic); |
141 | } |
142 | |
143 | template <typename Integer> |
144 | void atomic_notify_all(const std::atomic<Integer>* atomic) { |
145 | detail::atomic_notification::atomic_notify_all_impl(atomic); |
146 | } |
147 | |
148 | } // namespace folly |
149 | |