Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / outcome / detail / coroutine_support.ipp
1 /* Tells C++ coroutines about Outcome's result
2 (C) 2019 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 #ifdef BOOST_OUTCOME_FOUND_COROUTINE_HEADER
70 BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
71 namespace awaitables
72 {
73   namespace detail
74   {
75     struct error_type_not_found
76     {
77     };
78     struct exception_type_not_found
79     {
80     };
81     template <class T> struct type_found
82     {
83       using type = T;
84     };
85     template <class T, class U = typename T::error_type> constexpr inline type_found<U> extract_error_type(int /*unused*/) { return {}; }
86     template <class T> constexpr inline type_found<error_type_not_found> extract_error_type(...) { return {}; }
87     template <class T, class U = typename T::exception_type> constexpr inline type_found<U> extract_exception_type(int /*unused*/) { return {}; }
88     template <class T> constexpr inline type_found<exception_type_not_found> extract_exception_type(...) { return {}; }
89
90     BOOST_OUTCOME_TEMPLATE(class T, class U)
91     BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(std::is_constructible<U, T>::value))
92     inline bool try_set_error(T &e, U *result)
93     {
94       new(result) U(e);
95       return true;
96     }
97     template <class T> inline bool try_set_error(T & /*unused*/, ...) { return false; }
98     BOOST_OUTCOME_TEMPLATE(class T, class U)
99     BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(std::is_constructible<U, T>::value))
100     inline void set_or_rethrow(T &e, U *result) { new(result) U(e); }
101     template <class T> inline void set_or_rethrow(T &e, ...) { rethrow_exception(e); }
102     template <class T> class fake_atomic
103     {
104       T _v;
105
106     public:
107       constexpr fake_atomic(T v)
108           : _v(v)
109       {
110       }
111       T load(std::memory_order /*unused*/) { return _v; }
112       void store(T v, std::memory_order /*unused*/) { _v = v; }
113     };
114
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   }  // namespace detail
302
303 }  // namespace awaitables
304
305 BOOST_OUTCOME_V2_NAMESPACE_END
306
307 #endif
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