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