From b66754a29ed7a479e36fed89e0b45d10d5e71e71 Mon Sep 17 00:00:00 2001 From: Brian Gesiak Date: Tue, 26 Mar 2019 19:50:46 +0000 Subject: [PATCH] Revert "[coroutines] Add std::experimental::task type" This revision is causing build and test failures, such as http://lab.llvm.org:8011/builders/libcxx-libcxxabi-libunwind-armv8-linux/builds/648/steps/test.libcxx/logs/stdio, so I'll revert it. llvm-svn: 357023 --- libcxx/include/CMakeLists.txt | 1 - libcxx/include/experimental/__memory | 7 - libcxx/include/experimental/memory_resource | 8 + libcxx/include/experimental/task | 503 ------------------ libcxx/include/module.modulemap | 4 - .../experimental/task/awaitable_traits.hpp | 117 ---- libcxx/test/std/experimental/task/counted.hpp | 96 ---- .../test/std/experimental/task/lit.local.cfg | 9 - .../experimental/task/manual_reset_event.hpp | 127 ----- .../test/std/experimental/task/sync_wait.hpp | 284 ---------- .../task.basic/task_custom_allocator.pass.cpp | 230 -------- .../task/task.basic/task_of_value.pass.cpp | 70 --- .../task/task.basic/task_of_void.pass.cpp | 96 ---- .../task_parameter_lifetime.pass.cpp | 57 -- .../task_return_value_lifetime.pass.cpp | 86 --- 15 files changed, 8 insertions(+), 1687 deletions(-) delete mode 100644 libcxx/include/experimental/task delete mode 100644 libcxx/test/std/experimental/task/awaitable_traits.hpp delete mode 100644 libcxx/test/std/experimental/task/counted.hpp delete mode 100644 libcxx/test/std/experimental/task/lit.local.cfg delete mode 100644 libcxx/test/std/experimental/task/manual_reset_event.hpp delete mode 100644 libcxx/test/std/experimental/task/sync_wait.hpp delete mode 100644 libcxx/test/std/experimental/task/task.basic/task_custom_allocator.pass.cpp delete mode 100644 libcxx/test/std/experimental/task/task.basic/task_of_value.pass.cpp delete mode 100644 libcxx/test/std/experimental/task/task.basic/task_of_void.pass.cpp delete mode 100644 libcxx/test/std/experimental/task/task.lifetime/task_parameter_lifetime.pass.cpp delete mode 100644 libcxx/test/std/experimental/task/task.lifetime/task_return_value_lifetime.pass.cpp diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 0ceee6b5204a..9880115c86a4 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -86,7 +86,6 @@ set(files experimental/string experimental/string_view experimental/system_error - experimental/task experimental/tuple experimental/type_traits experimental/unordered_map diff --git a/libcxx/include/experimental/__memory b/libcxx/include/experimental/__memory index 6da6bef663ab..4cf8978468ce 100644 --- a/libcxx/include/experimental/__memory +++ b/libcxx/include/experimental/__memory @@ -73,13 +73,6 @@ struct __lfts_uses_alloc_ctor > {}; -// Round __s up to next multiple of __a. -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -size_t __aligned_allocation_size(size_t __s, size_t __a) _NOEXCEPT -{ - return (__s + __a - 1) & ~(__a - 1); -} - template inline _LIBCPP_INLINE_VISIBILITY void __lfts_user_alloc_construct( diff --git a/libcxx/include/experimental/memory_resource b/libcxx/include/experimental/memory_resource index 897bd1f88824..f999fb9befda 100644 --- a/libcxx/include/experimental/memory_resource +++ b/libcxx/include/experimental/memory_resource @@ -86,6 +86,14 @@ _LIBCPP_PUSH_MACROS _LIBCPP_BEGIN_NAMESPACE_LFTS_PMR +// Round __s up to next multiple of __a. +inline _LIBCPP_INLINE_VISIBILITY +size_t __aligned_allocation_size(size_t __s, size_t __a) _NOEXCEPT +{ + _LIBCPP_ASSERT(__s + __a > __s, "aligned allocation size overflows"); + return (__s + __a - 1) & ~(__a - 1); +} + // 8.5, memory.resource class _LIBCPP_TYPE_VIS memory_resource { diff --git a/libcxx/include/experimental/task b/libcxx/include/experimental/task deleted file mode 100644 index 2bdcaf2ba349..000000000000 --- a/libcxx/include/experimental/task +++ /dev/null @@ -1,503 +0,0 @@ -// -*- 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 -#include -#include - -#include -#include -#include - -#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) -#pragma GCC system_header -#endif - -#ifdef _LIBCPP_HAS_NO_COROUTINES -#if defined(_LIBCPP_WARNING) -_LIBCPP_WARNING(" cannot be used with this compiler") -#else -#warning cannot be used with this compiler -#endif -#endif - -_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES - -////// task - -template -class task; - -struct __task_promise_final_awaitable { - _LIBCPP_INLINE_VISIBILITY - _LIBCPP_CONSTEXPR bool await_ready() const _NOEXCEPT { return false; } - - template - _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 - 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 - 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 - 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(__frameStart) + - __get_dealloc_func_offset(__frameSize)); - } - - template - _LIBCPP_INLINE_VISIBILITY static _Alloc& - __get_allocator(void* __frameStart, size_t __frameSize) _NOEXCEPT { - return *reinterpret_cast<_Alloc*>( - static_cast(__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 - static void* operator new(size_t __size, allocator_arg_t, _Alloc& __alloc, - _Args&...) { - using _CharAlloc = - typename allocator_traits<_Alloc>::template rebind_alloc; - - _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 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(__pointer), - __paddedSize); - } else { - _CharAlloc __alloc; - __alloc.deallocate(static_cast(__pointer), __paddedSize); - } - }; - - // Copy the allocator into the heap frame (if required) - if constexpr (__allocator_needs_to_be_stored<_CharAlloc>) { - // task 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( - _VSTD::addressof(__get_allocator<_CharAlloc>(__pointer, __size)))) - _CharAlloc(_VSTD::move(__charAllocator)); - } - - return __pointer; - } - - template - 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 -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(&__exception_)) - exception_ptr(current_exception()); - __state_ = _State::__exception; -#else - _LIBCPP_ASSERT( - false, "task coroutine unexpectedly called unhandled_exception()"); -#endif - } - - // Only enable return_value() overload if _Tp is implicitly constructible from - // _Value - template ::value, int> = 0> - void return_value(_Value&& __value) - _NOEXCEPT_((is_nothrow_constructible_v<_Tp, _Value>)) { - __construct_value(static_cast<_Value&&>(__value)); - } - - template - 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> { - __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 - void __construct_value(_Args&&... __args) { - ::new (static_cast(_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 -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(&__exception_)) - exception_ptr(current_exception()); - __has_exception_ = true; -#else - _LIBCPP_ASSERT( - false, "task coroutine unexpectedly called unhandled_exception()"); -#endif - } - - void return_value(_Tp& __value) _NOEXCEPT { - ::new (static_cast(&__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 final : public __task_promise_base { - using _Handle = coroutine_handle<__task_promise>; - -public: - task 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 -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"); - 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"); - return _Awaiter{__coro_}; - } - -private: - friend class __task_promise<_Tp>; - - _LIBCPP_INLINE_VISIBILITY - task(_Handle __coro) _NOEXCEPT : __coro_(__coro) {} - - _Handle __coro_; -}; - -template -task<_Tp> __task_promise<_Tp>::get_return_object() _NOEXCEPT { - return task<_Tp>{_Handle::from_promise(*this)}; -} - -template -task<_Tp&> __task_promise<_Tp&>::get_return_object() _NOEXCEPT { - return task<_Tp&>{_Handle::from_promise(*this)}; -} - -task __task_promise::get_return_object() _NOEXCEPT { - return task{_Handle::from_promise(*this)}; -} - -_LIBCPP_END_NAMESPACE_EXPERIMENTAL_COROUTINES - -#endif diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 9601527540a3..bbfe90ed57f5 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -579,10 +579,6 @@ module std [system] { header "experimental/string" export * } - module task { - header "experimental/task" - export * - } module type_traits { header "experimental/type_traits" export * diff --git a/libcxx/test/std/experimental/task/awaitable_traits.hpp b/libcxx/test/std/experimental/task/awaitable_traits.hpp deleted file mode 100644 index 3d4ba16979a1..000000000000 --- a/libcxx/test/std/experimental/task/awaitable_traits.hpp +++ /dev/null @@ -1,117 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef _LIBCPP_TEST_EXPERIMENTAL_TASK_AWAITABLE_TRAITS -#define _LIBCPP_TEST_EXPERIMENTAL_TASK_AWAITABLE_TRAITS - -#include -#include - -_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES - -template -struct __is_coroutine_handle : std::false_type {}; - -template -struct __is_coroutine_handle> : - std::true_type -{}; - -template -struct __is_valid_await_suspend_result : - std::disjunction< - std::is_void<_Tp>, - std::is_same<_Tp, bool>, - __is_coroutine_handle<_Tp>> -{}; - -template -struct is_awaiter : std::false_type {}; - -template -struct is_awaiter<_Tp, std::void_t< - decltype(std::declval<_Tp&>().await_ready()), - decltype(std::declval<_Tp&>().await_resume()), - decltype(std::declval<_Tp&>().await_suspend( - std::declval>()))>> : - std::conjunction< - std::is_same().await_ready()), bool>, - __is_valid_await_suspend_result().await_suspend( - std::declval>()))>> -{}; - -template -constexpr bool is_awaiter_v = is_awaiter<_Tp>::value; - -template -struct __has_member_operator_co_await : std::false_type {}; - -template -struct __has_member_operator_co_await<_Tp, std::void_t().operator co_await())>> -: is_awaiter().operator co_await())> -{}; - -template -struct __has_non_member_operator_co_await : std::false_type {}; - -template -struct __has_non_member_operator_co_await<_Tp, std::void_t()))>> -: is_awaiter()))> -{}; - -template -struct is_awaitable : std::disjunction< - is_awaiter<_Tp>, - __has_member_operator_co_await<_Tp>, - __has_non_member_operator_co_await<_Tp>> -{}; - -template -constexpr bool is_awaitable_v = is_awaitable<_Tp>::value; - -template< - typename _Tp, - std::enable_if_t, int> = 0> -decltype(auto) get_awaiter(_Tp&& __awaitable) -{ - if constexpr (__has_member_operator_co_await<_Tp>::value) - { - return static_cast<_Tp&&>(__awaitable).operator co_await(); - } - else if constexpr (__has_non_member_operator_co_await<_Tp>::value) - { - return operator co_await(static_cast<_Tp&&>(__awaitable)); - } - else - { - return static_cast<_Tp&&>(__awaitable); - } -} - -template -struct await_result -{}; - -template -struct await_result<_Tp, std::enable_if_t>> -{ -private: - using __awaiter = decltype(get_awaiter(std::declval<_Tp>())); -public: - using type = decltype(std::declval<__awaiter&>().await_resume()); -}; - -template -using await_result_t = typename await_result<_Tp>::type; - -_LIBCPP_END_NAMESPACE_EXPERIMENTAL_COROUTINES - -#endif diff --git a/libcxx/test/std/experimental/task/counted.hpp b/libcxx/test/std/experimental/task/counted.hpp deleted file mode 100644 index 50658d223cf3..000000000000 --- a/libcxx/test/std/experimental/task/counted.hpp +++ /dev/null @@ -1,96 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef _LIBCPP_TEST_EXPERIMENTAL_TASK_COUNTED -#define _LIBCPP_TEST_EXPERIMENTAL_TASK_COUNTED - -class counted -{ -public: - - counted() : id_(nextId_++) - { - ++defaultConstructedCount_; - } - - counted(const counted& other) : id_(other.id_) - { - ++copyConstructedCount_; - } - - counted(counted&& other) : id_(std::exchange(other.id_, 0)) - { - ++moveConstructedCount_; - } - - ~counted() - { - ++destructedCount_; - } - - static void reset() - { - nextId_ = 1; - defaultConstructedCount_ = 0; - copyConstructedCount_ = 0; - moveConstructedCount_ = 0; - destructedCount_ = 0; - } - - static std::size_t active_instance_count() - { - return - defaultConstructedCount_ + - copyConstructedCount_ + - moveConstructedCount_ - - destructedCount_; - } - - static std::size_t copy_constructor_count() - { - return copyConstructedCount_; - } - - static std::size_t move_constructor_count() - { - return moveConstructedCount_; - } - - static std::size_t default_constructor_count() - { - return defaultConstructedCount_; - } - - static std::size_t destructor_count() - { - return destructedCount_; - } - - std::size_t id() const { return id_; } - -private: - std::size_t id_; - - static std::size_t nextId_; - static std::size_t defaultConstructedCount_; - static std::size_t copyConstructedCount_; - static std::size_t moveConstructedCount_; - static std::size_t destructedCount_; - -}; - -#define DEFINE_COUNTED_VARIABLES() \ - std::size_t counted::nextId_; \ - std::size_t counted::defaultConstructedCount_; \ - std::size_t counted::copyConstructedCount_; \ - std::size_t counted::moveConstructedCount_; \ - std::size_t counted::destructedCount_ - -#endif diff --git a/libcxx/test/std/experimental/task/lit.local.cfg b/libcxx/test/std/experimental/task/lit.local.cfg deleted file mode 100644 index a0b3de8afaa3..000000000000 --- a/libcxx/test/std/experimental/task/lit.local.cfg +++ /dev/null @@ -1,9 +0,0 @@ -# If the compiler doesn't support coroutines mark all of the tests under -# this directory as unsupported. Otherwise add the required `-fcoroutines-ts` -# flag. -if 'fcoroutines-ts' not in config.available_features: - config.unsupported = True -else: - import copy - config.test_format.cxx = copy.deepcopy(config.test_format.cxx) - config.test_format.cxx.compile_flags += ['-fcoroutines-ts'] diff --git a/libcxx/test/std/experimental/task/manual_reset_event.hpp b/libcxx/test/std/experimental/task/manual_reset_event.hpp deleted file mode 100644 index cfd532274957..000000000000 --- a/libcxx/test/std/experimental/task/manual_reset_event.hpp +++ /dev/null @@ -1,127 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef _LIBCPP_TEST_EXPERIMENTAL_TASK_MANUAL_RESET_EVENT -#define _LIBCPP_TEST_EXPERIMENTAL_TASK_MANUAL_RESET_EVENT - -#include -#include - -// manual_reset_event is a coroutine synchronisation tool that allows one -// coroutine to await the event object and if the event was not crrently -// in the 'set' state then will suspend the awaiting coroutine until some -// thread calls .set() on the event. -class manual_reset_event -{ - friend class _Awaiter; - - class _Awaiter - { - public: - - _Awaiter(const manual_reset_event* __event) noexcept - : __event_(__event) - {} - - bool await_ready() const noexcept - { - return __event_->is_set(); - } - - bool await_suspend(std::experimental::coroutine_handle<> __coro) noexcept - { - _LIBCPP_ASSERT( - __event_->__state_.load(std::memory_order_relaxed) != - __State::__not_set_waiting_coroutine, - "This manual_reset_event already has another coroutine awaiting it. " - "Only one awaiting coroutine is supported." - ); - - __event_->__awaitingCoroutine_ = __coro; - - // If the compare-exchange fails then this means that the event was - // already 'set' and so we should not suspend - this code path requires - // 'acquire' semantics so we have visibility of writes prior to the - // .set() operation that transitioned the event to the 'set' state. - // If the compare-exchange succeeds then this needs 'release' semantics - // so that a subsequent call to .set() has visibility of our writes - // to the coroutine frame and to __event_->__awaitingCoroutine_ after - // reading our write to __event_->__state_. - _State oldState = _State::__not_set; - return __event_->__state_.compare_exchange_strong( - oldState, - _State::__not_set_waiting_coroutine, - std::memory_order_release, - std::memory_order_acquire); - } - - void await_resume() const noexcept {} - - private: - const manual_reset_event* __event_; - }; - -public: - - manual_reset_event(bool __initiallySet = false) noexcept - : __state_(__initiallySet ? _State::__set : _State::__not_set) - {} - - bool is_set() const noexcept - { - return __state_.load(std::memory_order_acquire) == _State::__set; - } - - void set() noexcept - { - // Needs to be 'acquire' in case the old value was a waiting coroutine - // so that we have visibility of the writes to the coroutine frame in - // the current thrad before we resume it. - // Also needs to be 'release' in case the old value was 'not-set' so that - // another thread that subsequently awaits the - _State oldState = __state_.exchange(_State::__set, std::memory_order_acq_rel); - if (oldState == _State::__not_set_waiting_coroutine) - { - _VSTD::exchange(__awaitingCoroutine_, {}).resume(); - } - } - - void reset() noexcept - { - _LIBCPP_ASSERT( - __state_.load(std::memory_order_relaxed) != _State::__not_set_waiting_coroutine, - "Illegal to call reset() if a coroutine is currently awaiting the event."); - - // Note, we use 'relaxed' memory order here since it considered a - // data-race to call reset() concurrently either with operator co_await() - // or with set(). - __state_.store(_State::__not_set, std::memory_order_relaxed); - } - - auto operator co_await() const noexcept - { - return _Awaiter{ this }; - } - -private: - - enum class _State { - __not_set, - __not_set_waiting_coroutine, - __set - }; - - // TODO: Can we combine these two members into a single std::atomic? - mutable std::atomic<_State> __state_; - mutable std::experimental::coroutine_handle<> __awaitingCoroutine_; - -}; - -#endif diff --git a/libcxx/test/std/experimental/task/sync_wait.hpp b/libcxx/test/std/experimental/task/sync_wait.hpp deleted file mode 100644 index 42c629eb415b..000000000000 --- a/libcxx/test/std/experimental/task/sync_wait.hpp +++ /dev/null @@ -1,284 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef _LIBCPP_TEST_EXPERIMENTAL_TASK_SYNC_WAIT -#define _LIBCPP_TEST_EXPERIMENTAL_TASK_SYNC_WAIT - -#include -#include -#include -#include -#include - -#include "awaitable_traits.hpp" - -_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES - -// Thread-synchronisation helper that allows one thread to block in a call -// to .wait() until another thread signals the thread by calling .set(). -class __oneshot_event -{ -public: - __oneshot_event() : __isSet_(false) {} - - void set() noexcept - { - unique_lock __lock{ __mutex_ }; - __isSet_ = true; - __cv_.notify_all(); - } - - void wait() noexcept - { - unique_lock __lock{ __mutex_ }; - __cv_.wait(__lock, [this] { return __isSet_; }); - } - -private: - mutex __mutex_; - condition_variable __cv_; - bool __isSet_; -}; - -template -class __sync_wait_promise_base -{ -public: - - using __handle_t = coroutine_handle<_Derived>; - -private: - - struct _FinalAwaiter - { - bool await_ready() noexcept { return false; } - void await_suspend(__handle_t __coro) noexcept - { - __sync_wait_promise_base& __promise = __coro.promise(); - __promise.__event_.set(); - } - void await_resume() noexcept {} - }; - - friend struct _FinalAwaiter; - -public: - - __handle_t get_return_object() { return __handle(); } - suspend_always initial_suspend() { return {}; } - _FinalAwaiter final_suspend() { return {}; } - -private: - - __handle_t __handle() noexcept - { - return __handle_t::from_promise(static_cast<_Derived&>(*this)); - } - -protected: - - // Start the coroutine and then block waiting for it to finish. - void run() noexcept - { - __handle().resume(); - __event_.wait(); - } - -private: - - __oneshot_event __event_; - -}; - -template -class __sync_wait_promise final - : public __sync_wait_promise_base<__sync_wait_promise<_Tp>> -{ -public: - - __sync_wait_promise() : __state_(_State::__empty) {} - - ~__sync_wait_promise() - { - switch (__state_) - { - case _State::__empty: - case _State::__value: - break; -#ifndef _LIBCPP_NO_EXCEPTIONS - case _State::__exception: - __exception_.~exception_ptr(); - break; -#endif - } - } - - void return_void() noexcept - { - // Should be unreachable since coroutine should always - // suspend at `co_yield` point where it will be destroyed - // or will fail with an exception and bypass return_void() - // and call unhandled_exception() instead. - std::abort(); - } - - void unhandled_exception() noexcept - { -#ifndef _LIBCPP_NO_EXCEPTIONS - ::new (static_cast(&__exception_)) exception_ptr( - std::current_exception()); - __state_ = _State::__exception; -#else - _VSTD::abort(); -#endif - } - - auto yield_value(_Tp&& __value) noexcept - { - __valuePtr_ = std::addressof(__value); - __state_ = _State::__value; - return this->final_suspend(); - } - - _Tp&& get() - { - this->run(); - -#ifndef _LIBCPP_NO_EXCEPTIONS - if (__state_ == _State::__exception) - { - std::rethrow_exception(_VSTD::move(__exception_)); - } -#endif - - return static_cast<_Tp&&>(*__valuePtr_); - } - -private: - - enum class _State { - __empty, - __value, - __exception - }; - - _State __state_; - union { - std::add_pointer_t<_Tp> __valuePtr_; - std::exception_ptr __exception_; - }; - -}; - -template<> -struct __sync_wait_promise final - : public __sync_wait_promise_base<__sync_wait_promise> -{ -public: - - void unhandled_exception() noexcept - { -#ifndef _LIBCPP_NO_EXCEPTIONS - __exception_ = std::current_exception(); -#endif - } - - void return_void() noexcept {} - - void get() - { - this->run(); - -#ifndef _LIBCPP_NO_EXCEPTIONS - if (__exception_) - { - std::rethrow_exception(_VSTD::move(__exception_)); - } -#endif - } - -private: - - std::exception_ptr __exception_; - -}; - -template -class __sync_wait_task final -{ -public: - using promise_type = __sync_wait_promise<_Tp>; - -private: - using __handle_t = typename promise_type::__handle_t; - -public: - - __sync_wait_task(__handle_t __coro) noexcept : __coro_(__coro) {} - - ~__sync_wait_task() - { - _LIBCPP_ASSERT(__coro_, "Should always have a valid coroutine handle"); - __coro_.destroy(); - } - - decltype(auto) get() - { - return __coro_.promise().get(); - } -private: - __handle_t __coro_; -}; - -template -struct __remove_rvalue_reference -{ - using type = _Tp; -}; - -template -struct __remove_rvalue_reference<_Tp&&> -{ - using type = _Tp; -}; - -template -using __remove_rvalue_reference_t = - typename __remove_rvalue_reference<_Tp>::type; - -template< - typename _Awaitable, - typename _AwaitResult = await_result_t<_Awaitable>, - std::enable_if_t, int> = 0> -__sync_wait_task<_AwaitResult> __make_sync_wait_task(_Awaitable&& __awaitable) -{ - co_await static_cast<_Awaitable&&>(__awaitable); -} - -template< - typename _Awaitable, - typename _AwaitResult = await_result_t<_Awaitable>, - std::enable_if_t, int> = 0> -__sync_wait_task<_AwaitResult> __make_sync_wait_task(_Awaitable&& __awaitable) -{ - co_yield co_await static_cast<_Awaitable&&>(__awaitable); -} - -template -auto sync_wait(_Awaitable&& __awaitable) - -> __remove_rvalue_reference_t> -{ - return _VSTD_CORO::__make_sync_wait_task( - static_cast<_Awaitable&&>(__awaitable)).get(); -} - -_LIBCPP_END_NAMESPACE_EXPERIMENTAL_COROUTINES - -#endif diff --git a/libcxx/test/std/experimental/task/task.basic/task_custom_allocator.pass.cpp b/libcxx/test/std/experimental/task/task.basic/task_custom_allocator.pass.cpp deleted file mode 100644 index bc7e5989861e..000000000000 --- a/libcxx/test/std/experimental/task/task.basic/task_custom_allocator.pass.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03, c++11, c++14 - -#include -#include -#include -#include -#include -#include - -#include "../sync_wait.hpp" - -namespace coro = std::experimental::coroutines_v1; - -namespace -{ - static size_t allocator_instance_count = 0; - - // A custom allocator that tracks the number of allocator instances that - // have been constructed/destructed as well as the number of bytes that - // have been allocated/deallocated using the allocator. - template - class my_allocator { - public: - using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using is_always_equal = std::false_type; - - explicit my_allocator( - std::shared_ptr totalAllocated) noexcept - : totalAllocated_(std::move(totalAllocated)) - { - ++allocator_instance_count; - assert(totalAllocated_); - } - - my_allocator(const my_allocator& other) - : totalAllocated_(other.totalAllocated_) - { - ++allocator_instance_count; - } - - my_allocator(my_allocator&& other) - : totalAllocated_(std::move(other.totalAllocated_)) - { - ++allocator_instance_count; - } - - template - my_allocator(const my_allocator& other) - : totalAllocated_(other.totalAllocated_) - { - ++allocator_instance_count; - } - - template - my_allocator(my_allocator&& other) - : totalAllocated_(std::move(other.totalAllocated_)) - { - ++allocator_instance_count; - } - - ~my_allocator() - { - --allocator_instance_count; - } - - char* allocate(size_t n) { - const auto byteCount = n * sizeof(T); - void* p = std::malloc(byteCount); - if (!p) { - throw std::bad_alloc{}; - } - *totalAllocated_ += byteCount; - return static_cast(p); - } - - void deallocate(char* p, size_t n) { - const auto byteCount = n * sizeof(T); - *totalAllocated_ -= byteCount; - std::free(p); - } - private: - template - friend class my_allocator; - - std::shared_ptr totalAllocated_; - }; -} - -template -coro::task f(std::allocator_arg_t, [[maybe_unused]] Allocator alloc) -{ - co_return; -} - -void test_custom_allocator_is_destructed() -{ - auto totalAllocated = std::make_shared(0); - - assert(allocator_instance_count == 0); - - { - std::vector> tasks; - tasks.push_back( - f(std::allocator_arg, my_allocator{ totalAllocated })); - tasks.push_back( - f(std::allocator_arg, my_allocator{ totalAllocated })); - - assert(allocator_instance_count == 4); - assert(*totalAllocated > 0); - } - - assert(allocator_instance_count == 0); - assert(*totalAllocated == 0); -} - -void test_custom_allocator_type_rebinding() -{ - auto totalAllocated = std::make_shared(0); - { - std::vector> tasks; - tasks.emplace_back( - f(std::allocator_arg, my_allocator{ totalAllocated })); - coro::sync_wait(tasks[0]); - } - assert(*totalAllocated == 0); - assert(allocator_instance_count == 0); -} - -void test_mixed_custom_allocator_type_erasure() -{ - assert(allocator_instance_count == 0); - - // Show that different allocators can be used within a vector of tasks - // of the same type. ie. that the allocator is type-erased inside the - // coroutine. - std::vector> tasks; - tasks.push_back(f( - std::allocator_arg, std::allocator{})); - tasks.push_back(f( - std::allocator_arg, - std::experimental::pmr::polymorphic_allocator{ - std::experimental::pmr::new_delete_resource() })); - tasks.push_back(f( - std::allocator_arg, - my_allocator{ std::make_shared(0) })); - - assert(allocator_instance_count > 0); - - for (auto& t : tasks) - { - coro::sync_wait(t); - } - - tasks.clear(); - - assert(allocator_instance_count == 0); -} - -template -coro::task add_async(std::allocator_arg_t, [[maybe_unused]] Allocator alloc, int a, int b) -{ - co_return a + b; -} - -void test_task_custom_allocator_with_extra_args() -{ - std::vector> tasks; - - for (int i = 0; i < 5; ++i) { - tasks.push_back(add_async( - std::allocator_arg, - std::allocator{}, - i, 2 * i)); - } - - for (int i = 0; i < 5; ++i) - { - assert(sync_wait(std::move(tasks[i])) == 3 * i); - } -} - -struct some_type { - template - coro::task get_async(std::allocator_arg_t, [[maybe_unused]] Allocator alloc) { - co_return 42; - } - - template - coro::task add_async(std::allocator_arg_t, [[maybe_unused]] Allocator alloc, int a, int b) { - co_return a + b; - } -}; - -void test_task_custom_allocator_on_member_function() -{ - assert(allocator_instance_count == 0); - - auto totalAllocated = std::make_shared(0); - some_type obj; - assert(sync_wait(obj.get_async(std::allocator_arg, std::allocator{})) == 42); - assert(sync_wait(obj.get_async(std::allocator_arg, my_allocator{totalAllocated})) == 42); - assert(sync_wait(obj.add_async(std::allocator_arg, std::allocator{}, 2, 3)) == 5); - assert(sync_wait(obj.add_async(std::allocator_arg, my_allocator{totalAllocated}, 2, 3)) == 5); - - assert(allocator_instance_count == 0); - assert(*totalAllocated == 0); -} - -int main() -{ - test_custom_allocator_is_destructed(); - test_custom_allocator_type_rebinding(); - test_mixed_custom_allocator_type_erasure(); - test_task_custom_allocator_with_extra_args(); - test_task_custom_allocator_on_member_function(); - - return 0; -} diff --git a/libcxx/test/std/experimental/task/task.basic/task_of_value.pass.cpp b/libcxx/test/std/experimental/task/task.basic/task_of_value.pass.cpp deleted file mode 100644 index 0d5a8c9f619a..000000000000 --- a/libcxx/test/std/experimental/task/task.basic/task_of_value.pass.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03, c++11, c++14 - -#include -#include -#include -#include -#include "../sync_wait.hpp" - -void test_returning_move_only_type() -{ - auto move_only_async = - [](bool x) -> std::experimental::task> { - if (x) { - auto p = std::make_unique(123); - co_return p; // Should be implicit std::move(p) here. - } - - co_return std::make_unique(456); - }; - - assert(*sync_wait(move_only_async(true)) == 123); - assert(*sync_wait(move_only_async(false)) == 456); -} - -void test_co_return_with_curly_braces() -{ - auto t = []() -> std::experimental::task> - { - co_return { 123, "test" }; - }(); - - auto result = sync_wait(std::move(t)); - - assert(std::get<0>(result) == 123); - assert(std::get<1>(result) == "test"); -} - -void test_co_return_by_initialiser_list() -{ - auto t = []() -> std::experimental::task> - { - co_return { 2, 10, -1 }; - }(); - - auto result = sync_wait(std::move(t)); - - assert(result.size() == 3); - assert(result[0] == 2); - assert(result[1] == 10); - assert(result[2] == -1); -} - -int main() -{ - test_returning_move_only_type(); - test_co_return_with_curly_braces(); - test_co_return_by_initialiser_list(); - - return 0; -} diff --git a/libcxx/test/std/experimental/task/task.basic/task_of_void.pass.cpp b/libcxx/test/std/experimental/task/task.basic/task_of_void.pass.cpp deleted file mode 100644 index de860b5db6a8..000000000000 --- a/libcxx/test/std/experimental/task/task.basic/task_of_void.pass.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03, c++11, c++14 - -#include -#include "../manual_reset_event.hpp" -#include "../sync_wait.hpp" - -#include -#include - -namespace coro = std::experimental::coroutines_v1; - -static bool has_f_executed = false; - -static coro::task f() -{ - has_f_executed = true; - co_return; -} - -static void test_coroutine_executes_lazily() -{ - coro::task t = f(); - assert(!has_f_executed); - coro::sync_wait(t); - assert(has_f_executed); -} - -static std::optional last_value_passed_to_g; - -static coro::task g(int a) -{ - last_value_passed_to_g = a; - co_return; -} - -void test_coroutine_accepts_arguments() -{ - auto t = g(123); - assert(!last_value_passed_to_g); - coro::sync_wait(t); - assert(last_value_passed_to_g); - assert(*last_value_passed_to_g == 123); -} - -int shared_value = 0; -int read_value = 0; - -coro::task consume_async(manual_reset_event& event) -{ - co_await event; - read_value = shared_value; -} - -void produce(manual_reset_event& event) -{ - shared_value = 101; - event.set(); -} - -void test_async_completion() -{ - manual_reset_event e; - std::thread t1{ [&e] - { - sync_wait(consume_async(e)); - }}; - - assert(read_value == 0); - - std::thread t2{ [&e] { produce(e); }}; - - t1.join(); - - assert(read_value == 101); - - t2.join(); -} - -int main() -{ - test_coroutine_executes_lazily(); - test_coroutine_accepts_arguments(); - test_async_completion(); - - return 0; -} diff --git a/libcxx/test/std/experimental/task/task.lifetime/task_parameter_lifetime.pass.cpp b/libcxx/test/std/experimental/task/task.lifetime/task_parameter_lifetime.pass.cpp deleted file mode 100644 index bc1bd432244d..000000000000 --- a/libcxx/test/std/experimental/task/task.lifetime/task_parameter_lifetime.pass.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03, c++11, c++14 - -#include -#include - -#include "../counted.hpp" -#include "../sync_wait.hpp" - -DEFINE_COUNTED_VARIABLES(); - -void test_parameter_lifetime() -{ - counted::reset(); - - auto f = [](counted c) -> std::experimental::task - { - co_return c.id(); - }; - - { - auto t = f({}); - - assert(counted::active_instance_count() == 1); - assert(counted::copy_constructor_count() == 0); - assert(counted::move_constructor_count() <= 2); // Ideally <= 1 - - auto id = sync_wait(t); - assert(id == 1); - - assert(counted::active_instance_count() == 1); - assert(counted::copy_constructor_count() == 0); - - // We are relying on C++17 copy-elision when passing the temporary counter - // into f(). Then f() must move the parameter into the coroutine frame by - // calling the move-constructor. This move could also potentially be - // elided by the - assert(counted::move_constructor_count() <= 1); - } - - assert(counted::active_instance_count() == 0); -} - -int main() -{ - test_parameter_lifetime(); - return 0; -} diff --git a/libcxx/test/std/experimental/task/task.lifetime/task_return_value_lifetime.pass.cpp b/libcxx/test/std/experimental/task/task.lifetime/task_return_value_lifetime.pass.cpp deleted file mode 100644 index cf528f506603..000000000000 --- a/libcxx/test/std/experimental/task/task.lifetime/task_return_value_lifetime.pass.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03, c++11, c++14 - -#include -#include -#include - -#include "../counted.hpp" -#include "../sync_wait.hpp" - -DEFINE_COUNTED_VARIABLES(); - -void test_return_value_lifetime() -{ - counted::reset(); - - auto f = [](bool x) -> std::experimental::task - { - if (x) { - counted c; - co_return std::move(c); - } - co_return {}; - }; - - { - auto t = f(true); - - assert(counted::active_instance_count() == 0); - assert(counted::copy_constructor_count() == 0); - assert(counted::move_constructor_count() == 0); - - { - auto c = sync_wait(std::move(t)); - assert(c.id() == 1); - - assert(counted::active_instance_count() == 2); - assert(counted::copy_constructor_count() == 0); - assert(counted::move_constructor_count() > 0); - assert(counted::default_constructor_count() == 1); - } - - // The result value in 't' is still alive until 't' destructs. - assert(counted::active_instance_count() == 1); - } - - assert(counted::active_instance_count() == 0); - - counted::reset(); - - { - auto t = f(false); - - assert(counted::active_instance_count() == 0); - assert(counted::copy_constructor_count() == 0); - assert(counted::move_constructor_count() == 0); - - { - auto c = sync_wait(std::move(t)); - assert(c.id() == 1); - - assert(counted::active_instance_count() == 2); - assert(counted::copy_constructor_count() == 0); - assert(counted::move_constructor_count() > 0); - assert(counted::default_constructor_count() == 1); - } - - // The result value in 't' is still alive until 't' destructs. - assert(counted::active_instance_count() == 1); - } -} - -int main() -{ - test_return_value_lifetime(); - return 0; -}