1 | // Copyright 2019 Google LLC |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | #ifndef dap_future_h |
16 | #define dap_future_h |
17 | |
18 | #include <condition_variable> |
19 | #include <memory> |
20 | #include <mutex> |
21 | |
22 | namespace dap { |
23 | |
24 | // internal functionality |
25 | namespace detail { |
26 | template <typename T> |
27 | struct promise_state { |
28 | T val; |
29 | std::mutex mutex; |
30 | std::condition_variable cv; |
31 | bool hasVal = false; |
32 | }; |
33 | } // namespace detail |
34 | |
35 | // forward declaration |
36 | template <typename T> |
37 | class promise; |
38 | |
39 | // future_status is the enumeration returned by future::wait_for and |
40 | // future::wait_until. |
41 | enum class future_status { |
42 | ready, |
43 | timeout, |
44 | }; |
45 | |
46 | // future is a minimal reimplementation of std::future, that does not suffer |
47 | // from TSAN false positives. See: |
48 | // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204 |
49 | template <typename T> |
50 | class future { |
51 | public: |
52 | using State = detail::promise_state<T>; |
53 | |
54 | // constructors |
55 | inline future() = default; |
56 | inline future(future&&) = default; |
57 | |
58 | // valid() returns true if the future has an internal state. |
59 | bool valid() const; |
60 | |
61 | // get() blocks until the future has a valid result, and returns it. |
62 | // The future must have a valid internal state to call this method. |
63 | inline T get(); |
64 | |
65 | // wait() blocks until the future has a valid result. |
66 | // The future must have a valid internal state to call this method. |
67 | void wait() const; |
68 | |
69 | // wait_for() blocks until the future has a valid result, or the timeout is |
70 | // reached. |
71 | // The future must have a valid internal state to call this method. |
72 | template <class Rep, class Period> |
73 | future_status wait_for( |
74 | const std::chrono::duration<Rep, Period>& timeout) const; |
75 | |
76 | // wait_until() blocks until the future has a valid result, or the timeout is |
77 | // reached. |
78 | // The future must have a valid internal state to call this method. |
79 | template <class Clock, class Duration> |
80 | future_status wait_until( |
81 | const std::chrono::time_point<Clock, Duration>& timeout) const; |
82 | |
83 | private: |
84 | friend promise<T>; |
85 | future(const future&) = delete; |
86 | inline future(const std::shared_ptr<State>& state); |
87 | |
88 | std::shared_ptr<State> state = std::make_shared<State>(); |
89 | }; |
90 | |
91 | template <typename T> |
92 | future<T>::future(const std::shared_ptr<State>& s) : state(s) {} |
93 | |
94 | template <typename T> |
95 | bool future<T>::valid() const { |
96 | return static_cast<bool>(state); |
97 | } |
98 | |
99 | template <typename T> |
100 | T future<T>::get() { |
101 | std::unique_lock<std::mutex> lock(state->mutex); |
102 | state->cv.wait(lock, [&] { return state->hasVal; }); |
103 | return state->val; |
104 | } |
105 | |
106 | template <typename T> |
107 | void future<T>::wait() const { |
108 | std::unique_lock<std::mutex> lock(state->mutex); |
109 | state->cv.wait(lock, [&] { return state->hasVal; }); |
110 | } |
111 | |
112 | template <typename T> |
113 | template <class Rep, class Period> |
114 | future_status future<T>::wait_for( |
115 | const std::chrono::duration<Rep, Period>& timeout) const { |
116 | std::unique_lock<std::mutex> lock(state->mutex); |
117 | return state->cv.wait_for(lock, timeout, [&] { return state->hasVal; }) |
118 | ? future_status::ready |
119 | : future_status::timeout; |
120 | } |
121 | |
122 | template <typename T> |
123 | template <class Clock, class Duration> |
124 | future_status future<T>::wait_until( |
125 | const std::chrono::time_point<Clock, Duration>& timeout) const { |
126 | std::unique_lock<std::mutex> lock(state->mutex); |
127 | return state->cv.wait_until(lock, timeout, [&] { return state->hasVal; }) |
128 | ? future_status::ready |
129 | : future_status::timeout; |
130 | } |
131 | |
132 | // promise is a minimal reimplementation of std::promise, that does not suffer |
133 | // from TSAN false positives. See: |
134 | // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204 |
135 | template <typename T> |
136 | class promise { |
137 | public: |
138 | // constructors |
139 | inline promise() = default; |
140 | inline promise(promise&& other) = default; |
141 | inline promise(const promise& other) = default; |
142 | |
143 | // set_value() stores value to the shared state. |
144 | // set_value() must only be called once. |
145 | inline void set_value(const T& value) const; |
146 | inline void set_value(T&& value) const; |
147 | |
148 | // get_future() returns a future sharing this promise's state. |
149 | future<T> get_future(); |
150 | |
151 | private: |
152 | using State = detail::promise_state<T>; |
153 | std::shared_ptr<State> state = std::make_shared<State>(); |
154 | }; |
155 | |
156 | template <typename T> |
157 | future<T> promise<T>::get_future() { |
158 | return future<T>(state); |
159 | } |
160 | |
161 | template <typename T> |
162 | void promise<T>::set_value(const T& value) const { |
163 | std::unique_lock<std::mutex> lock(state->mutex); |
164 | state->val = value; |
165 | state->hasVal = true; |
166 | state->cv.notify_all(); |
167 | } |
168 | |
169 | template <typename T> |
170 | void promise<T>::set_value(T&& value) const { |
171 | std::unique_lock<std::mutex> lock(state->mutex); |
172 | state->val = std::move(value); |
173 | state->hasVal = true; |
174 | state->cv.notify_all(); |
175 | } |
176 | |
177 | } // namespace dap |
178 | |
179 | #endif // dap_future_h |
180 | |