1 /* Tells C++ coroutines about Outcome's result
2 (C) 2019-2020 Niall Douglas <http://www.nedproductions.biz/> (12 commits)
6 Boost Software License - Version 1.0 - August 17th, 2003
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:
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.
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.
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
35 #ifndef BOOST_OUTCOME_DETAIL_COROUTINE_SUPPORT_HPP
36 #define BOOST_OUTCOME_DETAIL_COROUTINE_SUPPORT_HPP
42 #if __has_include(<coroutine>)
44 BOOST_OUTCOME_V2_NAMESPACE_BEGIN
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
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
69 BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
74 struct error_type_not_found
77 struct exception_type_not_found
80 template <class T> struct type_found
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 {}; }
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)
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
106 constexpr fake_atomic(T v)
110 T load(std::memory_order /*unused*/) { return _v; }
111 void store(T v, std::memory_order /*unused*/) { _v = v; }
114 #ifdef BOOST_OUTCOME_FOUND_COROUTINE_HEADER
115 template <class Awaitable, bool suspend_initial, bool use_atomic, bool is_void> struct outcome_promise_type
117 using container_type = typename Awaitable::container_type;
118 using result_set_type = std::conditional_t<use_atomic, std::atomic<bool>, fake_atomic<bool>>;
120 BOOST_OUTCOME_V2_NAMESPACE::detail::empty_type _default{};
121 container_type result;
123 result_set_type result_set{false};
124 coroutine_handle<> continuation;
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()
133 if(result_set.load(std::memory_order_acquire))
135 result.~container_type();
138 auto get_return_object() { return Awaitable{*this}; }
139 void return_value(container_type &&value)
141 assert(!result_set.load(std::memory_order_acquire));
142 if(result_set.load(std::memory_order_acquire))
144 result.~container_type();
146 new(&result) container_type(static_cast<container_type &&>(value));
147 result_set.store(true, std::memory_order_release);
149 void return_value(const container_type &value)
151 assert(!result_set.load(std::memory_order_acquire));
152 if(result_set.load(std::memory_order_acquire))
154 result.~container_type();
156 new(&result) container_type(value);
157 result_set.store(true, std::memory_order_release);
159 void unhandled_exception()
161 assert(!result_set.load(std::memory_order_acquire));
162 if(result_set.load(std::memory_order_acquire))
164 result.~container_type();
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))
172 detail::set_or_rethrow(e, &result);
177 result_set.store(true, std::memory_order_release);
179 auto initial_suspend() noexcept
183 bool await_ready() noexcept { return !suspend_initial; }
184 void await_resume() noexcept {}
185 void await_suspend(coroutine_handle<> /*unused*/) {}
193 bool await_ready() noexcept { return false; }
194 void await_resume() noexcept {}
195 void await_suspend(coroutine_handle<outcome_promise_type> self)
197 if(self.promise().continuation)
199 return self.promise().continuation.resume();
206 template <class Awaitable, bool suspend_initial, bool use_atomic> struct outcome_promise_type<Awaitable, suspend_initial, use_atomic, true>
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;
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}; }
222 assert(!result_set.load(std::memory_order_acquire));
223 result_set.store(true, std::memory_order_release);
225 void unhandled_exception()
227 assert(!result_set.load(std::memory_order_acquire));
228 std::rethrow_exception(std::current_exception());
230 auto initial_suspend() noexcept
234 bool await_ready() noexcept { return !suspend_initial; }
235 void await_resume() noexcept {}
236 void await_suspend(coroutine_handle<> /*unused*/) {}
244 bool await_ready() noexcept { return false; }
245 void await_resume() noexcept {}
246 void await_suspend(coroutine_handle<outcome_promise_type> self)
248 if(self.promise().continuation)
250 return self.promise().continuation.resume();
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*/) {}
260 template <class Cont, bool suspend_initial, bool use_atomic> struct BOOST_OUTCOME_NODISCARD awaitable
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;
266 awaitable(awaitable &&o) noexcept
267 : _h(static_cast<coroutine_handle<promise_type> &&>(o._h))
271 awaitable(const awaitable &o) = delete;
272 awaitable &operator=(awaitable &&) = delete; // as per P1056
273 awaitable &operator=(const awaitable &) = delete;
281 explicit awaitable(promise_type &p)
282 : _h(coroutine_handle<promise_type>::from_promise(p))
285 bool await_ready() noexcept { return _h.promise().result_set.load(std::memory_order_acquire); }
286 container_type await_resume()
288 assert(_h.promise().result_set.load(std::memory_order_acquire));
289 if(!_h.promise().result_set.load(std::memory_order_acquire))
293 return detail::move_result_from_promise_if_not_void(_h.promise());
295 void await_suspend(coroutine_handle<> cont)
297 _h.promise().continuation = cont;
302 } // namespace detail
304 } // namespace awaitables
306 BOOST_OUTCOME_V2_NAMESPACE_END
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
315 template <class T> using eager = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, false, false>;
317 /*! AWAITING HUGO JSON CONVERSION TOOL
318 SIGNATURE NOT RECOGNISED
320 template <class T> using atomic_eager = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, false, true>;
322 /*! AWAITING HUGO JSON CONVERSION TOOL
323 SIGNATURE NOT RECOGNISED
325 template <class T> using lazy = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, true, false>;
327 /*! AWAITING HUGO JSON CONVERSION TOOL
328 SIGNATURE NOT RECOGNISED
330 template <class T> using atomic_lazy = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, true, true>;
332 BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_END