|
@@ -0,0 +1,503 @@
|
|
|
+// -*- C++ -*-
|
|
|
+//===------------------------------- task ---------------------------------===//
|
|
|
+//
|
|
|
+// The LLVM Compiler Infrastructure
|
|
|
+//
|
|
|
+// This file is distributed under the University of Illinois Open Source
|
|
|
+// License. See LICENSE.TXT for details.
|
|
|
+//
|
|
|
+//===----------------------------------------------------------------------===//
|
|
|
+
|
|
|
+#ifndef _LIBCPP_EXPERIMENTAL_TASK
|
|
|
+#define _LIBCPP_EXPERIMENTAL_TASK
|
|
|
+
|
|
|
+#include <experimental/__config>
|
|
|
+#include <experimental/__memory>
|
|
|
+#include <experimental/coroutine>
|
|
|
+
|
|
|
+#include <exception>
|
|
|
+#include <type_traits>
|
|
|
+#include <utility>
|
|
|
+
|
|
|
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
|
|
+#pragma GCC system_header
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef _LIBCPP_HAS_NO_COROUTINES
|
|
|
+#if defined(_LIBCPP_WARNING)
|
|
|
+_LIBCPP_WARNING("<experimental/task> cannot be used with this compiler")
|
|
|
+#else
|
|
|
+#warning <experimental/task> cannot be used with this compiler
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+
|
|
|
+_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES
|
|
|
+
|
|
|
+////// task<T>
|
|
|
+
|
|
|
+template <typename _Tp = void>
|
|
|
+class task;
|
|
|
+
|
|
|
+struct __task_promise_final_awaitable {
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ _LIBCPP_CONSTEXPR bool await_ready() const _NOEXCEPT { return false; }
|
|
|
+
|
|
|
+ template <typename _TaskPromise>
|
|
|
+ _LIBCPP_INLINE_VISIBILITY coroutine_handle<>
|
|
|
+ await_suspend(coroutine_handle<_TaskPromise> __coro) const _NOEXCEPT {
|
|
|
+ _LIBCPP_ASSERT(
|
|
|
+ __coro.promise().__continuation_,
|
|
|
+ "Coroutine completed without a valid continuation attached.");
|
|
|
+ return __coro.promise().__continuation_;
|
|
|
+ }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ void await_resume() const _NOEXCEPT {}
|
|
|
+};
|
|
|
+
|
|
|
+class _LIBCPP_TYPE_VIS __task_promise_base {
|
|
|
+ using _DeallocFunc = void(void* __ptr, size_t __size) _NOEXCEPT;
|
|
|
+
|
|
|
+ template <typename _Alloc>
|
|
|
+ static constexpr bool __allocator_needs_to_be_stored =
|
|
|
+ !allocator_traits<_Alloc>::is_always_equal::value ||
|
|
|
+ !is_default_constructible_v<_Alloc>;
|
|
|
+
|
|
|
+ static _LIBCPP_CONSTEXPR size_t
|
|
|
+ __get_dealloc_func_offset(size_t __frameSize) _NOEXCEPT {
|
|
|
+ return _VSTD_LFTS::__aligned_allocation_size(__frameSize,
|
|
|
+ alignof(_DeallocFunc*));
|
|
|
+ }
|
|
|
+
|
|
|
+ static _LIBCPP_CONSTEXPR size_t
|
|
|
+ __get_padded_frame_size(size_t __frameSize) _NOEXCEPT {
|
|
|
+ return __get_dealloc_func_offset(__frameSize) + sizeof(_DeallocFunc*);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename _Alloc>
|
|
|
+ static _LIBCPP_CONSTEXPR size_t
|
|
|
+ __get_allocator_offset(size_t __frameSize) _NOEXCEPT {
|
|
|
+ return _VSTD_LFTS::__aligned_allocation_size(
|
|
|
+ __get_padded_frame_size(__frameSize), alignof(_Alloc));
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename _Alloc>
|
|
|
+ static _LIBCPP_CONSTEXPR size_t
|
|
|
+ __get_padded_frame_size_with_allocator(size_t __frameSize) _NOEXCEPT {
|
|
|
+ if constexpr (__allocator_needs_to_be_stored<_Alloc>) {
|
|
|
+ return __get_allocator_offset<_Alloc>(__frameSize) + sizeof(_Alloc);
|
|
|
+ } else {
|
|
|
+ return __get_padded_frame_size(__frameSize);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ static _DeallocFunc*& __get_dealloc_func(void* __frameStart,
|
|
|
+ size_t __frameSize) _NOEXCEPT {
|
|
|
+ return *reinterpret_cast<_DeallocFunc**>(
|
|
|
+ static_cast<char*>(__frameStart) +
|
|
|
+ __get_dealloc_func_offset(__frameSize));
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename _Alloc>
|
|
|
+ _LIBCPP_INLINE_VISIBILITY static _Alloc&
|
|
|
+ __get_allocator(void* __frameStart, size_t __frameSize) _NOEXCEPT {
|
|
|
+ return *reinterpret_cast<_Alloc*>(
|
|
|
+ static_cast<char*>(__frameStart) +
|
|
|
+ __get_allocator_offset<_Alloc>(__frameSize));
|
|
|
+ }
|
|
|
+
|
|
|
+public:
|
|
|
+ __task_promise_base() _NOEXCEPT = default;
|
|
|
+
|
|
|
+ // Explicitly disable special member functions.
|
|
|
+ __task_promise_base(const __task_promise_base&) = delete;
|
|
|
+ __task_promise_base(__task_promise_base&&) = delete;
|
|
|
+ __task_promise_base& operator=(const __task_promise_base&) = delete;
|
|
|
+ __task_promise_base& operator=(__task_promise_base&&) = delete;
|
|
|
+
|
|
|
+ static void* operator new(size_t __size) {
|
|
|
+ // Allocate space for an extra pointer immediately after __size that holds
|
|
|
+ // the type-erased deallocation function.
|
|
|
+ void* __pointer = ::operator new(__get_padded_frame_size(__size));
|
|
|
+
|
|
|
+ _DeallocFunc*& __deallocFunc = __get_dealloc_func(__pointer, __size);
|
|
|
+ __deallocFunc = [](void* __pointer, size_t __size) _NOEXCEPT {
|
|
|
+ ::operator delete(__pointer, __get_padded_frame_size(__size));
|
|
|
+ };
|
|
|
+
|
|
|
+ return __pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename _Alloc, typename... _Args>
|
|
|
+ static void* operator new(size_t __size, allocator_arg_t, _Alloc& __alloc,
|
|
|
+ _Args&...) {
|
|
|
+ using _CharAlloc =
|
|
|
+ typename allocator_traits<_Alloc>::template rebind_alloc<char>;
|
|
|
+
|
|
|
+ _CharAlloc __charAllocator{__alloc};
|
|
|
+
|
|
|
+ void* __pointer = __charAllocator.allocate(
|
|
|
+ __get_padded_frame_size_with_allocator<_CharAlloc>(__size));
|
|
|
+
|
|
|
+ _DeallocFunc*& __deallocFunc = __get_dealloc_func(__pointer, __size);
|
|
|
+ __deallocFunc = [](void* __pointer, size_t __size) _NOEXCEPT {
|
|
|
+ // Allocators are required to not throw from their move constructors
|
|
|
+ // however they aren't required to be declared noexcept so we can't
|
|
|
+ // actually check this with a static_assert.
|
|
|
+ //
|
|
|
+ // static_assert(is_nothrow_move_constructible<_Alloc>::value,
|
|
|
+ // "task<T> coroutine custom allocator requires a noexcept "
|
|
|
+ // "move constructor");
|
|
|
+
|
|
|
+ size_t __paddedSize =
|
|
|
+ __get_padded_frame_size_with_allocator<_CharAlloc>(__size);
|
|
|
+
|
|
|
+ if constexpr (__allocator_needs_to_be_stored<_CharAlloc>) {
|
|
|
+ _CharAlloc& __allocatorInFrame =
|
|
|
+ __get_allocator<_CharAlloc>(__pointer, __size);
|
|
|
+ _CharAlloc __allocatorOnStack = _VSTD::move(__allocatorInFrame);
|
|
|
+ __allocatorInFrame.~_CharAlloc();
|
|
|
+ // Allocator requirements state that deallocate() must not throw.
|
|
|
+ // See [allocator.requirements] from C++ standard.
|
|
|
+ // We are relying on that here.
|
|
|
+ __allocatorOnStack.deallocate(static_cast<char*>(__pointer),
|
|
|
+ __paddedSize);
|
|
|
+ } else {
|
|
|
+ _CharAlloc __alloc;
|
|
|
+ __alloc.deallocate(static_cast<char*>(__pointer), __paddedSize);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // Copy the allocator into the heap frame (if required)
|
|
|
+ if constexpr (__allocator_needs_to_be_stored<_CharAlloc>) {
|
|
|
+ // task<T> coroutine custom allocation requires the copy constructor to
|
|
|
+ // not throw but we can't rely on it being declared noexcept.
|
|
|
+ // If it did throw we'd leak the allocation here.
|
|
|
+ ::new (static_cast<void*>(
|
|
|
+ _VSTD::addressof(__get_allocator<_CharAlloc>(__pointer, __size))))
|
|
|
+ _CharAlloc(_VSTD::move(__charAllocator));
|
|
|
+ }
|
|
|
+
|
|
|
+ return __pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename _This, typename _Alloc, typename... _Args>
|
|
|
+ static void* operator new(size_t __size, _This&, allocator_arg_t __allocArg, _Alloc& __alloc,
|
|
|
+ _Args&...) {
|
|
|
+ return __task_promise_base::operator new(__size, __allocArg, __alloc);
|
|
|
+ }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ static void operator delete(void* __pointer, size_t __size)_NOEXCEPT {
|
|
|
+ __get_dealloc_func(__pointer, __size)(__pointer, __size);
|
|
|
+ }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ suspend_always initial_suspend() const _NOEXCEPT { return {}; }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ __task_promise_final_awaitable final_suspend() _NOEXCEPT { return {}; }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ void __set_continuation(coroutine_handle<> __continuation) {
|
|
|
+ _LIBCPP_ASSERT(!__continuation_, "task already has a continuation");
|
|
|
+ __continuation_ = __continuation;
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ friend struct __task_promise_final_awaitable;
|
|
|
+
|
|
|
+ coroutine_handle<> __continuation_;
|
|
|
+};
|
|
|
+
|
|
|
+template <typename _Tp>
|
|
|
+class _LIBCPP_TEMPLATE_VIS __task_promise final : public __task_promise_base {
|
|
|
+ using _Handle = coroutine_handle<__task_promise>;
|
|
|
+
|
|
|
+public:
|
|
|
+ __task_promise() _NOEXCEPT : __state_(_State::__no_value) {}
|
|
|
+
|
|
|
+ ~__task_promise() {
|
|
|
+ switch (__state_) {
|
|
|
+ case _State::__value:
|
|
|
+ __value_.~_Tp();
|
|
|
+ break;
|
|
|
+#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
|
+ case _State::__exception:
|
|
|
+ __exception_.~exception_ptr();
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ case _State::__no_value:
|
|
|
+ break;
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ task<_Tp> get_return_object() _NOEXCEPT;
|
|
|
+
|
|
|
+ void unhandled_exception() _NOEXCEPT {
|
|
|
+#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
|
+ ::new (static_cast<void*>(&__exception_))
|
|
|
+ exception_ptr(current_exception());
|
|
|
+ __state_ = _State::__exception;
|
|
|
+#else
|
|
|
+ _LIBCPP_ASSERT(
|
|
|
+ false, "task<T> coroutine unexpectedly called unhandled_exception()");
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ // Only enable return_value() overload if _Tp is implicitly constructible from
|
|
|
+ // _Value
|
|
|
+ template <typename _Value,
|
|
|
+ enable_if_t<is_convertible<_Value, _Tp>::value, int> = 0>
|
|
|
+ void return_value(_Value&& __value)
|
|
|
+ _NOEXCEPT_((is_nothrow_constructible_v<_Tp, _Value>)) {
|
|
|
+ __construct_value(static_cast<_Value&&>(__value));
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename _Value>
|
|
|
+ auto return_value(std::initializer_list<_Value> __initializer) _NOEXCEPT_(
|
|
|
+ (is_nothrow_constructible_v<_Tp, std::initializer_list<_Value>>))
|
|
|
+ -> std::enable_if_t<
|
|
|
+ std::is_constructible_v<_Tp, std::initializer_list<_Value>>> {
|
|
|
+ __construct_value(_VSTD::move(__initializer));
|
|
|
+ }
|
|
|
+
|
|
|
+ auto return_value(_Tp&& __value)
|
|
|
+ _NOEXCEPT_((is_nothrow_move_constructible_v<_Tp>))
|
|
|
+ -> std::enable_if_t<std::is_move_constructible_v<_Tp>> {
|
|
|
+ __construct_value(static_cast<_Tp&&>(__value));
|
|
|
+ }
|
|
|
+
|
|
|
+ _Tp& __lvalue_result() {
|
|
|
+ __throw_if_exception();
|
|
|
+ return __value_;
|
|
|
+ }
|
|
|
+
|
|
|
+ _Tp __rvalue_result() {
|
|
|
+ __throw_if_exception();
|
|
|
+ return static_cast<_Tp&&>(__value_);
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ template <typename... _Args>
|
|
|
+ void __construct_value(_Args&&... __args) {
|
|
|
+ ::new (static_cast<void*>(_VSTD::addressof(__value_)))
|
|
|
+ _Tp(static_cast<_Args&&>(__args)...);
|
|
|
+
|
|
|
+ // Only set __state_ after successfully constructing the value.
|
|
|
+ // If constructor throws then state will be updated by
|
|
|
+ // unhandled_exception().
|
|
|
+ __state_ = _State::__value;
|
|
|
+ }
|
|
|
+
|
|
|
+ void __throw_if_exception() {
|
|
|
+#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
|
+ if (__state_ == _State::__exception) {
|
|
|
+ rethrow_exception(__exception_);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ enum class _State { __no_value, __value, __exception };
|
|
|
+
|
|
|
+ _State __state_ = _State::__no_value;
|
|
|
+ union {
|
|
|
+ char __empty_;
|
|
|
+ _Tp __value_;
|
|
|
+ exception_ptr __exception_;
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+template <typename _Tp>
|
|
|
+class __task_promise<_Tp&> final : public __task_promise_base {
|
|
|
+ using _Ptr = _Tp*;
|
|
|
+ using _Handle = coroutine_handle<__task_promise>;
|
|
|
+
|
|
|
+public:
|
|
|
+ __task_promise() _NOEXCEPT = default;
|
|
|
+
|
|
|
+ ~__task_promise() {
|
|
|
+#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
|
+ if (__has_exception_) {
|
|
|
+ __exception_.~exception_ptr();
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ task<_Tp&> get_return_object() _NOEXCEPT;
|
|
|
+
|
|
|
+ void unhandled_exception() _NOEXCEPT {
|
|
|
+#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
|
+ ::new (static_cast<void*>(&__exception_))
|
|
|
+ exception_ptr(current_exception());
|
|
|
+ __has_exception_ = true;
|
|
|
+#else
|
|
|
+ _LIBCPP_ASSERT(
|
|
|
+ false, "task<T> coroutine unexpectedly called unhandled_exception()");
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ void return_value(_Tp& __value) _NOEXCEPT {
|
|
|
+ ::new (static_cast<void*>(&__pointer_)) _Ptr(_VSTD::addressof(__value));
|
|
|
+ }
|
|
|
+
|
|
|
+ _Tp& __lvalue_result() {
|
|
|
+ __throw_if_exception();
|
|
|
+ return *__pointer_;
|
|
|
+ }
|
|
|
+
|
|
|
+ _Tp& __rvalue_result() { return __lvalue_result(); }
|
|
|
+
|
|
|
+private:
|
|
|
+ void __throw_if_exception() {
|
|
|
+#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
|
+ if (__has_exception_) {
|
|
|
+ rethrow_exception(__exception_);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ union {
|
|
|
+ char __empty_;
|
|
|
+ _Ptr __pointer_;
|
|
|
+ exception_ptr __exception_;
|
|
|
+ };
|
|
|
+ bool __has_exception_ = false;
|
|
|
+};
|
|
|
+
|
|
|
+template <>
|
|
|
+class __task_promise<void> final : public __task_promise_base {
|
|
|
+ using _Handle = coroutine_handle<__task_promise>;
|
|
|
+
|
|
|
+public:
|
|
|
+ task<void> get_return_object() _NOEXCEPT;
|
|
|
+
|
|
|
+ void return_void() _NOEXCEPT {}
|
|
|
+
|
|
|
+ void unhandled_exception() _NOEXCEPT {
|
|
|
+#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
|
+ __exception_ = current_exception();
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ void __lvalue_result() { __throw_if_exception(); }
|
|
|
+
|
|
|
+ void __rvalue_result() { __throw_if_exception(); }
|
|
|
+
|
|
|
+private:
|
|
|
+ void __throw_if_exception() {
|
|
|
+#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
|
+ if (__exception_) {
|
|
|
+ rethrow_exception(__exception_);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ exception_ptr __exception_;
|
|
|
+};
|
|
|
+
|
|
|
+template <typename _Tp>
|
|
|
+class _LIBCPP_TEMPLATE_VIS _LIBCPP_NODISCARD_AFTER_CXX17 task {
|
|
|
+public:
|
|
|
+ using promise_type = __task_promise<_Tp>;
|
|
|
+
|
|
|
+private:
|
|
|
+ using _Handle = coroutine_handle<__task_promise<_Tp>>;
|
|
|
+
|
|
|
+ class _AwaiterBase {
|
|
|
+ public:
|
|
|
+ _AwaiterBase(_Handle __coro) _NOEXCEPT : __coro_(__coro) {}
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ bool await_ready() const { return __coro_.done(); }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ _Handle await_suspend(coroutine_handle<> __continuation) const {
|
|
|
+ __coro_.promise().__set_continuation(__continuation);
|
|
|
+ return __coro_;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected:
|
|
|
+ _Handle __coro_;
|
|
|
+ };
|
|
|
+
|
|
|
+public:
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ task(task&& __other) _NOEXCEPT
|
|
|
+ : __coro_(_VSTD::exchange(__other.__coro_, {})) {}
|
|
|
+
|
|
|
+ task(const task&) = delete;
|
|
|
+ task& operator=(const task&) = delete;
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ ~task() {
|
|
|
+ if (__coro_)
|
|
|
+ __coro_.destroy();
|
|
|
+ }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ void swap(task& __other) _NOEXCEPT { _VSTD::swap(__coro_, __other.__coro_); }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ auto operator co_await() & {
|
|
|
+ class _Awaiter : public _AwaiterBase {
|
|
|
+ public:
|
|
|
+ using _AwaiterBase::_AwaiterBase;
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ decltype(auto) await_resume() {
|
|
|
+ return this->__coro_.promise().__lvalue_result();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ _LIBCPP_ASSERT(__coro_,
|
|
|
+ "Undefined behaviour to co_await an invalid task<T>");
|
|
|
+ return _Awaiter{__coro_};
|
|
|
+ }
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ auto operator co_await() && {
|
|
|
+ class _Awaiter : public _AwaiterBase {
|
|
|
+ public:
|
|
|
+ using _AwaiterBase::_AwaiterBase;
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ decltype(auto) await_resume() {
|
|
|
+ return this->__coro_.promise().__rvalue_result();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ _LIBCPP_ASSERT(__coro_,
|
|
|
+ "Undefined behaviour to co_await an invalid task<T>");
|
|
|
+ return _Awaiter{__coro_};
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ friend class __task_promise<_Tp>;
|
|
|
+
|
|
|
+ _LIBCPP_INLINE_VISIBILITY
|
|
|
+ task(_Handle __coro) _NOEXCEPT : __coro_(__coro) {}
|
|
|
+
|
|
|
+ _Handle __coro_;
|
|
|
+};
|
|
|
+
|
|
|
+template <typename _Tp>
|
|
|
+task<_Tp> __task_promise<_Tp>::get_return_object() _NOEXCEPT {
|
|
|
+ return task<_Tp>{_Handle::from_promise(*this)};
|
|
|
+}
|
|
|
+
|
|
|
+template <typename _Tp>
|
|
|
+task<_Tp&> __task_promise<_Tp&>::get_return_object() _NOEXCEPT {
|
|
|
+ return task<_Tp&>{_Handle::from_promise(*this)};
|
|
|
+}
|
|
|
+
|
|
|
+task<void> __task_promise<void>::get_return_object() _NOEXCEPT {
|
|
|
+ return task<void>{_Handle::from_promise(*this)};
|
|
|
+}
|
|
|
+
|
|
|
+_LIBCPP_END_NAMESPACE_EXPERIMENTAL_COROUTINES
|
|
|
+
|
|
|
+#endif
|