]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/outcome/detail/coroutine_support.ipp
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / boost / boost / outcome / detail / coroutine_support.ipp
1 /* Tells C++ coroutines about Outcome's result
2 (C) 2019-2020 Niall Douglas <http://www.nedproductions.biz/> (12 commits)
3 File Created: Oct 2019
4
5
6 Boost Software License - Version 1.0 - August 17th, 2003
7
8 Permission is hereby granted, free of charge, to any person or organization
9 obtaining a copy of the software and accompanying documentation covered by
10 this license (the "Software") to use, reproduce, display, distribute,
11 execute, and transmit the Software, and to prepare derivative works of the
12 Software, and to permit third-parties to whom the Software is furnished to
13 do so, all subject to the following:
14
15 The copyright notices in the Software and this entire statement, including
16 the above license grant, this restriction and the following disclaimer,
17 must be included in all copies of the Software, in whole or in part, and
18 all derivative works of the Software, unless such copies or derivative
19 works are solely in the form of machine-executable object code generated by
20 a source language processor.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
25 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
26 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
27 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 DEALINGS IN THE SOFTWARE.
29 */
30
31 #ifndef BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_BEGIN
32 #error This header must only be included by outcome/coroutine_support.hpp or outcome/experimental/coroutine_support.hpp
33 #endif
34
35 #ifndef BOOST_OUTCOME_DETAIL_COROUTINE_SUPPORT_HPP
36 #define BOOST_OUTCOME_DETAIL_COROUTINE_SUPPORT_HPP
37
38 #include <atomic>
39 #include <cassert>
40
41 #if __cpp_coroutines
42 #if __has_include(<coroutine>)
43 #include <coroutine>
44 BOOST_OUTCOME_V2_NAMESPACE_BEGIN
45 namespace awaitables
46 {
47 template <class Promise = void> using coroutine_handle = std::coroutine_handle<Promise>;
48 template <class... Args> using coroutine_traits = std::coroutine_traits<Args...>;
49 using std::suspend_always;
50 using std::suspend_never;
51 } // namespace awaitables
52 BOOST_OUTCOME_V2_NAMESPACE_END
53 #define BOOST_OUTCOME_FOUND_COROUTINE_HEADER 1
54 #elif __has_include(<experimental/coroutine>)
55 #include <experimental/coroutine>
56 BOOST_OUTCOME_V2_NAMESPACE_BEGIN
57 namespace awaitables
58 {
59 template <class Promise = void> using coroutine_handle = std::experimental::coroutine_handle<Promise>;
60 template <class... Args> using coroutine_traits = std::experimental::coroutine_traits<Args...>;
61 using std::experimental::suspend_always;
62 using std::experimental::suspend_never;
63 } // namespace awaitables
64 BOOST_OUTCOME_V2_NAMESPACE_END
65 #define BOOST_OUTCOME_FOUND_COROUTINE_HEADER 1
66 #endif
67 #endif
68
69 BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
70 namespace awaitables
71 {
72 namespace detail
73 {
74 struct error_type_not_found
75 {
76 };
77 struct exception_type_not_found
78 {
79 };
80 template <class T> struct type_found
81 {
82 using type = T;
83 };
84 template <class T, class U = typename T::error_type> constexpr inline type_found<U> extract_error_type(int /*unused*/) { return {}; }
85 template <class T> constexpr inline type_found<error_type_not_found> extract_error_type(...) { return {}; }
86 template <class T, class U = typename T::exception_type> constexpr inline type_found<U> extract_exception_type(int /*unused*/) { return {}; }
87 template <class T> constexpr inline type_found<exception_type_not_found> extract_exception_type(...) { return {}; }
88
89 BOOST_OUTCOME_TEMPLATE(class T, class U)
90 BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(std::is_constructible<U, T>::value))
91 inline bool try_set_error(T &e, U *result)
92 {
93 new(result) U(e);
94 return true;
95 }
96 template <class T> inline bool try_set_error(T & /*unused*/, ...) { return false; }
97 BOOST_OUTCOME_TEMPLATE(class T, class U)
98 BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(std::is_constructible<U, T>::value))
99 inline void set_or_rethrow(T &e, U *result) { new(result) U(e); }
100 template <class T> inline void set_or_rethrow(T &e, ...) { rethrow_exception(e); }
101 template <class T> class fake_atomic
102 {
103 T _v;
104
105 public:
106 constexpr fake_atomic(T v)
107 : _v(v)
108 {
109 }
110 T load(std::memory_order /*unused*/) { return _v; }
111 void store(T v, std::memory_order /*unused*/) { _v = v; }
112 };
113
114 #ifdef BOOST_OUTCOME_FOUND_COROUTINE_HEADER
115 template <class Awaitable, bool suspend_initial, bool use_atomic, bool is_void> struct outcome_promise_type
116 {
117 using container_type = typename Awaitable::container_type;
118 using result_set_type = std::conditional_t<use_atomic, std::atomic<bool>, fake_atomic<bool>>;
119 union {
120 BOOST_OUTCOME_V2_NAMESPACE::detail::empty_type _default{};
121 container_type result;
122 };
123 result_set_type result_set{false};
124 coroutine_handle<> continuation;
125
126 outcome_promise_type() {}
127 outcome_promise_type(const outcome_promise_type &) = delete;
128 outcome_promise_type(outcome_promise_type &&) = delete;
129 outcome_promise_type &operator=(const outcome_promise_type &) = delete;
130 outcome_promise_type &operator=(outcome_promise_type &&) = delete;
131 ~outcome_promise_type()
132 {
133 if(result_set.load(std::memory_order_acquire))
134 {
135 result.~container_type();
136 }
137 }
138 auto get_return_object() { return Awaitable{*this}; }
139 void return_value(container_type &&value)
140 {
141 assert(!result_set.load(std::memory_order_acquire));
142 if(result_set.load(std::memory_order_acquire))
143 {
144 result.~container_type();
145 }
146 new(&result) container_type(static_cast<container_type &&>(value));
147 result_set.store(true, std::memory_order_release);
148 }
149 void return_value(const container_type &value)
150 {
151 assert(!result_set.load(std::memory_order_acquire));
152 if(result_set.load(std::memory_order_acquire))
153 {
154 result.~container_type();
155 }
156 new(&result) container_type(value);
157 result_set.store(true, std::memory_order_release);
158 }
159 void unhandled_exception()
160 {
161 assert(!result_set.load(std::memory_order_acquire));
162 if(result_set.load(std::memory_order_acquire))
163 {
164 result.~container_type();
165 }
166 #ifndef BOOST_NO_EXCEPTIONS
167 auto e = std::current_exception();
168 auto ec = detail::error_from_exception(static_cast<decltype(e) &&>(e), {});
169 // Try to set error code first
170 if(!detail::error_is_set(ec) || !detail::try_set_error(ec, &result))
171 {
172 detail::set_or_rethrow(e, &result);
173 }
174 #else
175 std::terminate();
176 #endif
177 result_set.store(true, std::memory_order_release);
178 }
179 auto initial_suspend() noexcept
180 {
181 struct awaiter
182 {
183 bool await_ready() noexcept { return !suspend_initial; }
184 void await_resume() noexcept {}
185 void await_suspend(coroutine_handle<> /*unused*/) {}
186 };
187 return awaiter{};
188 }
189 auto final_suspend()
190 {
191 struct awaiter
192 {
193 bool await_ready() noexcept { return false; }
194 void await_resume() noexcept {}
195 void await_suspend(coroutine_handle<outcome_promise_type> self)
196 {
197 if(self.promise().continuation)
198 {
199 return self.promise().continuation.resume();
200 }
201 }
202 };
203 return awaiter{};
204 }
205 };
206 template <class Awaitable, bool suspend_initial, bool use_atomic> struct outcome_promise_type<Awaitable, suspend_initial, use_atomic, true>
207 {
208 using container_type = void;
209 using result_set_type = std::conditional_t<use_atomic, std::atomic<bool>, fake_atomic<bool>>;
210 result_set_type result_set{false};
211 coroutine_handle<> continuation;
212
213 outcome_promise_type() {}
214 outcome_promise_type(const outcome_promise_type &) = delete;
215 outcome_promise_type(outcome_promise_type &&) = delete;
216 outcome_promise_type &operator=(const outcome_promise_type &) = delete;
217 outcome_promise_type &operator=(outcome_promise_type &&) = delete;
218 ~outcome_promise_type() = default;
219 auto get_return_object() { return Awaitable{*this}; }
220 void return_void()
221 {
222 assert(!result_set.load(std::memory_order_acquire));
223 result_set.store(true, std::memory_order_release);
224 }
225 void unhandled_exception()
226 {
227 assert(!result_set.load(std::memory_order_acquire));
228 std::rethrow_exception(std::current_exception());
229 }
230 auto initial_suspend() noexcept
231 {
232 struct awaiter
233 {
234 bool await_ready() noexcept { return !suspend_initial; }
235 void await_resume() noexcept {}
236 void await_suspend(coroutine_handle<> /*unused*/) {}
237 };
238 return awaiter{};
239 }
240 auto final_suspend()
241 {
242 struct awaiter
243 {
244 bool await_ready() noexcept { return false; }
245 void await_resume() noexcept {}
246 void await_suspend(coroutine_handle<outcome_promise_type> self)
247 {
248 if(self.promise().continuation)
249 {
250 return self.promise().continuation.resume();
251 }
252 }
253 };
254 return awaiter{};
255 }
256 };
257 template <class Awaitable, bool suspend_initial, bool use_atomic> constexpr inline auto move_result_from_promise_if_not_void(outcome_promise_type<Awaitable, suspend_initial, use_atomic, false> &p) { return static_cast<typename Awaitable::container_type &&>(p.result); }
258 template <class Awaitable, bool suspend_initial, bool use_atomic> constexpr inline void move_result_from_promise_if_not_void(outcome_promise_type<Awaitable, suspend_initial, use_atomic, true> & /*unused*/) {}
259
260 template <class Cont, bool suspend_initial, bool use_atomic> struct BOOST_OUTCOME_NODISCARD awaitable
261 {
262 using container_type = Cont;
263 using promise_type = outcome_promise_type<awaitable, suspend_initial, use_atomic, std::is_void<container_type>::value>;
264 coroutine_handle<promise_type> _h;
265
266 awaitable(awaitable &&o) noexcept
267 : _h(static_cast<coroutine_handle<promise_type> &&>(o._h))
268 {
269 o._h = nullptr;
270 }
271 awaitable(const awaitable &o) = delete;
272 awaitable &operator=(awaitable &&) = delete; // as per P1056
273 awaitable &operator=(const awaitable &) = delete;
274 ~awaitable()
275 {
276 if(_h)
277 {
278 _h.destroy();
279 }
280 }
281 explicit awaitable(promise_type &p)
282 : _h(coroutine_handle<promise_type>::from_promise(p))
283 {
284 }
285 bool await_ready() noexcept { return _h.promise().result_set.load(std::memory_order_acquire); }
286 container_type await_resume()
287 {
288 assert(_h.promise().result_set.load(std::memory_order_acquire));
289 if(!_h.promise().result_set.load(std::memory_order_acquire))
290 {
291 std::terminate();
292 }
293 return detail::move_result_from_promise_if_not_void(_h.promise());
294 }
295 void await_suspend(coroutine_handle<> cont)
296 {
297 _h.promise().continuation = cont;
298 _h.resume();
299 }
300 };
301 #endif
302 } // namespace detail
303
304 } // namespace awaitables
305
306 BOOST_OUTCOME_V2_NAMESPACE_END
307
308 #endif
309
310 #ifdef BOOST_OUTCOME_FOUND_COROUTINE_HEADER
311 BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_EXPORT_BEGIN
312 /*! AWAITING HUGO JSON CONVERSION TOOL
313 SIGNATURE NOT RECOGNISED
314 */
315 template <class T> using eager = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, false, false>;
316
317 /*! AWAITING HUGO JSON CONVERSION TOOL
318 SIGNATURE NOT RECOGNISED
319 */
320 template <class T> using atomic_eager = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, false, true>;
321
322 /*! AWAITING HUGO JSON CONVERSION TOOL
323 SIGNATURE NOT RECOGNISED
324 */
325 template <class T> using lazy = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, true, false>;
326
327 /*! AWAITING HUGO JSON CONVERSION TOOL
328 SIGNATURE NOT RECOGNISED
329 */
330 template <class T> using atomic_lazy = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, true, true>;
331
332 BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_END
333 #endif