diff --git a/libs/coroutines/include/hpx/coroutines/detail/coroutine_accessor.hpp b/libs/coroutines/include/hpx/coroutines/detail/coroutine_accessor.hpp index 5f944eec7db7..6422066d877a 100644 --- a/libs/coroutines/include/hpx/coroutines/detail/coroutine_accessor.hpp +++ b/libs/coroutines/include/hpx/coroutines/detail/coroutine_accessor.hpp @@ -31,20 +31,9 @@ #define HPX_RUNTIME_THREADS_COROUTINES_DETAIL_COROUTINE_ACCESSOR_HPP namespace hpx { namespace threads { namespace coroutines { namespace detail { + struct coroutine_accessor { - template - static void acquire(Coroutine& x) - { - x.acquire(); - } - - template - static void release(Coroutine& x) - { - x.release(); - } - template static typename Coroutine::impl_ptr get_impl(Coroutine& x) { diff --git a/libs/coroutines/include/hpx/coroutines/detail/coroutine_self.hpp b/libs/coroutines/include/hpx/coroutines/detail/coroutine_self.hpp index c7fce04436f6..f4bc6f3870be 100644 --- a/libs/coroutines/include/hpx/coroutines/detail/coroutine_self.hpp +++ b/libs/coroutines/include/hpx/coroutines/detail/coroutine_self.hpp @@ -44,12 +44,13 @@ #include namespace hpx { namespace threads { namespace coroutines { namespace detail { + class coroutine_self { public: HPX_NON_COPYABLE(coroutine_self); - private: + protected: // store the current this and write it to the TSS on exit struct reset_self_on_exit { @@ -68,17 +69,18 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail { }; public: - friend struct detail::coroutine_accessor; + using thread_id_type = hpx::threads::thread_id; - typedef coroutine_impl impl_type; - typedef impl_type* impl_ptr; // Note, no reference counting here. - typedef impl_type::thread_id_type thread_id_type; + using result_type = std::pair; + using arg_type = thread_state_ex_enum; - typedef impl_type::result_type result_type; - typedef impl_type::arg_type arg_type; + using yield_decorator_type = + util::function_nonser; - typedef util::function_nonser - yield_decorator_type; + explicit coroutine_self(coroutine_self* next_self = nullptr) + : next_self_(next_self) + { + } arg_type yield(result_type arg = result_type()) { @@ -87,20 +89,6 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail { yield_impl(std::move(arg)); } - arg_type yield_impl(result_type arg) - { - HPX_ASSERT(m_pimpl); - - this->m_pimpl->bind_result(arg); - - { - reset_self_on_exit on_exit(this); - this->m_pimpl->yield(); - } - - return *m_pimpl->args(); - } - template yield_decorator_type decorate_yield(F&& f) { @@ -129,67 +117,33 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail { return tmp; } - thread_id_type get_thread_id() const - { - HPX_ASSERT(m_pimpl); - return m_pimpl->get_thread_id(); - } + virtual ~coroutine_self() = default; - std::size_t get_thread_phase() const - { -#if defined(HPX_HAVE_THREAD_PHASE_INFORMATION) - HPX_ASSERT(m_pimpl); - return m_pimpl->get_thread_phase(); -#else - return 0; -#endif - } + virtual arg_type yield_impl(result_type arg) = 0; - std::ptrdiff_t get_available_stack_space() - { -#if defined(HPX_HAVE_THREADS_GET_STACK_POINTER) - return m_pimpl->get_available_stack_space(); -#else - return (std::numeric_limits::max)(); -#endif - } + virtual thread_id_type get_thread_id() const = 0; - explicit coroutine_self( - impl_type* pimpl, coroutine_self* next_self = nullptr) - : m_pimpl(pimpl) - , next_self_(next_self) - { - } + virtual std::size_t get_thread_phase() const = 0; - std::size_t get_thread_data() const - { - HPX_ASSERT(m_pimpl); - return m_pimpl->get_thread_data(); - } - std::size_t set_thread_data(std::size_t data) - { - HPX_ASSERT(m_pimpl); - return m_pimpl->set_thread_data(data); - } + virtual std::ptrdiff_t get_available_stack_space() = 0; -#if defined(HPX_HAVE_THREAD_LOCAL_STORAGE) - tss_storage* get_thread_tss_data() - { - HPX_ASSERT(m_pimpl); - return m_pimpl->get_thread_tss_data(false); - } + virtual std::size_t get_thread_data() const = 0; + virtual std::size_t set_thread_data(std::size_t data) = 0; - tss_storage* get_or_create_thread_tss_data() - { - HPX_ASSERT(m_pimpl); - return m_pimpl->get_thread_tss_data(true); - } -#endif + virtual tss_storage* get_thread_tss_data() = 0; + virtual tss_storage* get_or_create_thread_tss_data() = 0; + + virtual std::size_t& get_continuation_recursion_count() = 0; + + // access coroutines context object + using impl_type = coroutine_impl; + using impl_ptr = impl_type*; - std::size_t& get_continuation_recursion_count() + private: + friend struct coroutine_accessor; + virtual impl_ptr get_impl() { - HPX_ASSERT(m_pimpl); - return m_pimpl->get_continuation_recursion_count(); + return nullptr; } public: @@ -206,14 +160,27 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail { private: yield_decorator_type yield_decorator_; + coroutine_self* next_self_; + }; - impl_ptr get_impl() + //////////////////////////////////////////////////////////////////////////// + struct reset_self_on_exit + { + reset_self_on_exit( + coroutine_self* val, coroutine_self* old_val = nullptr) + : old_self(old_val) { - return m_pimpl; + coroutine_self::set_self(val); } - impl_ptr m_pimpl; - coroutine_self* next_self_; + + ~reset_self_on_exit() + { + coroutine_self::set_self(old_self); + } + + coroutine_self* old_self; }; + }}}} // namespace hpx::threads::coroutines::detail -#endif /*HPX_RUNTIME_THREADS_COROUTINES_DETAIL_SELF_HPP*/ +#endif diff --git a/libs/coroutines/include/hpx/coroutines/detail/coroutine_stackful_self.hpp b/libs/coroutines/include/hpx/coroutines/detail/coroutine_stackful_self.hpp new file mode 100644 index 000000000000..f6ae446c1d8d --- /dev/null +++ b/libs/coroutines/include/hpx/coroutines/detail/coroutine_stackful_self.hpp @@ -0,0 +1,120 @@ +// Copyright (c) 2019 Hartmut Kaiser +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef HPX_RUNTIME_THREADS_COROUTINES_DETAIL_STACKFUL_SELF_HPP +#define HPX_RUNTIME_THREADS_COROUTINES_DETAIL_STACKFUL_SELF_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace hpx { namespace threads { namespace coroutines { namespace detail { + + class coroutine_stackful_self : public coroutine_self + { + public: + explicit coroutine_stackful_self( + impl_type* pimpl, coroutine_self* next_self = nullptr) + : coroutine_self(next_self) + , pimpl_(pimpl) + { + } + + arg_type yield_impl(result_type arg) override + { + HPX_ASSERT(pimpl_); + + this->pimpl_->bind_result(arg); + + { + reset_self_on_exit on_exit(this); + this->pimpl_->yield(); + } + + return *pimpl_->args(); + } + + thread_id_type get_thread_id() const override + { + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_id(); + } + + std::size_t get_thread_phase() const override + { +#if defined(HPX_HAVE_THREAD_PHASE_INFORMATION) + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_phase(); +#else + return 0; +#endif + } + + std::ptrdiff_t get_available_stack_space() override + { +#if defined(HPX_HAVE_THREADS_GET_STACK_POINTER) + return pimpl_->get_available_stack_space(); +#else + return (std::numeric_limits::max)(); +#endif + } + + std::size_t get_thread_data() const override + { + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_data(); + } + std::size_t set_thread_data(std::size_t data) override + { + HPX_ASSERT(pimpl_); + return pimpl_->set_thread_data(data); + } + + tss_storage* get_thread_tss_data() override + { +#if defined(HPX_HAVE_THREAD_LOCAL_STORAGE) + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_tss_data(false); +#else + return nullptr; +#endif + } + + tss_storage* get_or_create_thread_tss_data() override + { +#if defined(HPX_HAVE_THREAD_LOCAL_STORAGE) + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_tss_data(true); +#else + return nullptr; +#endif + } + + std::size_t& get_continuation_recursion_count() override + { + HPX_ASSERT(pimpl_); + return pimpl_->get_continuation_recursion_count(); + } + + private: + coroutine_impl* get_impl() override + { + return pimpl_; + } + coroutine_impl* pimpl_; + }; +}}}} // namespace hpx::threads::coroutines::detail + +#endif /*HPX_RUNTIME_THREADS_COROUTINES_DETAIL_SELF_HPP*/ diff --git a/libs/coroutines/include/hpx/coroutines/detail/coroutine_stackless_self.hpp b/libs/coroutines/include/hpx/coroutines/detail/coroutine_stackless_self.hpp new file mode 100644 index 000000000000..dcd2eb6ee715 --- /dev/null +++ b/libs/coroutines/include/hpx/coroutines/detail/coroutine_stackless_self.hpp @@ -0,0 +1,108 @@ +// Copyright (c) 2019 Hartmut Kaiser +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef HPX_RUNTIME_THREADS_COROUTINES_DETAIL_STACKLESS_SELF_HPP +#define HPX_RUNTIME_THREADS_COROUTINES_DETAIL_STACKLESS_SELF_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace hpx { namespace threads { namespace coroutines { + + class stackless_coroutine; +}}} // namespace hpx::threads::coroutines + +namespace hpx { namespace threads { namespace coroutines { namespace detail { + + class coroutine_stackless_self : public coroutine_self + { + public: + explicit coroutine_stackless_self(stackless_coroutine* pimpl) + : coroutine_self(nullptr) + , pimpl_(pimpl) + { + } + + arg_type yield_impl(result_type arg) override + { + // stackless coroutines don't support suspension + HPX_ASSERT(false); + return threads::wait_abort; + } + + thread_id_type get_thread_id() const override + { + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_id(); + } + + std::size_t get_thread_phase() const override + { +#if defined(HPX_HAVE_THREAD_PHASE_INFORMATION) + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_phase(); +#else + return 0; +#endif + } + + std::ptrdiff_t get_available_stack_space() override + { + return (std::numeric_limits::max)(); + } + + std::size_t get_thread_data() const override + { + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_data(); + } + std::size_t set_thread_data(std::size_t data) override + { + HPX_ASSERT(pimpl_); + return pimpl_->set_thread_data(data); + } + + tss_storage* get_thread_tss_data() override + { +#if defined(HPX_HAVE_THREAD_LOCAL_STORAGE) + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_tss_data(false); +#else + return nullptr; +#endif + } + + tss_storage* get_or_create_thread_tss_data() override + { +#if defined(HPX_HAVE_THREAD_LOCAL_STORAGE) + HPX_ASSERT(pimpl_); + return pimpl_->get_thread_tss_data(true); +#else + return nullptr; +#endif + } + + std::size_t& get_continuation_recursion_count() override + { + HPX_ASSERT(pimpl_); + return pimpl_->get_continuation_recursion_count(); + } + + private: + stackless_coroutine* pimpl_; + }; +}}}} // namespace hpx::threads::coroutines::detail + +#endif diff --git a/libs/coroutines/include/hpx/coroutines/stackless_coroutine.hpp b/libs/coroutines/include/hpx/coroutines/stackless_coroutine.hpp index 74e4c3c1ac57..3b07f8d31e25 100644 --- a/libs/coroutines/include/hpx/coroutines/stackless_coroutine.hpp +++ b/libs/coroutines/include/hpx/coroutines/stackless_coroutine.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ namespace hpx { namespace threads { namespace coroutines { #else , thread_data_(0) #endif + , continuation_recursion_count_(0) { } @@ -186,29 +188,7 @@ namespace hpx { namespace threads { namespace coroutines { friend struct reset_on_exit; public: - HPX_FORCEINLINE result_type operator()(arg_type arg = arg_type()) - { - HPX_ASSERT(is_ready()); - - result_type result( - thread_state_enum::terminated, invalid_thread_id); - - { - reset_on_exit on_exit{*this}; - - HPX_UNUSED(on_exit); - - result = f_(arg); // invoke wrapped function - - // we always have to run to completion - HPX_ASSERT(result.first == threads::terminated); - - reset_tss(); - } - - reset(); - return result; - } + HPX_FORCEINLINE result_type operator()(arg_type arg = arg_type()); explicit operator bool() const { @@ -225,6 +205,11 @@ namespace hpx { namespace threads { namespace coroutines { return (std::numeric_limits::max)(); } + std::size_t& get_continuation_recursion_count() + { + return continuation_recursion_count_; + } + protected: functor_type f_; context_state state_; @@ -238,8 +223,43 @@ namespace hpx { namespace threads { namespace coroutines { #else mutable std::size_t thread_data_; #endif + std::size_t continuation_recursion_count_; }; }}} // namespace hpx::threads::coroutines +//////////////////////////////////////////////////////////////////////////////// +#include + +namespace hpx { namespace threads { namespace coroutines { + + HPX_FORCEINLINE stackless_coroutine::result_type stackless_coroutine:: + operator()(arg_type arg) + { + HPX_ASSERT(is_ready()); + + result_type result(thread_state_enum::terminated, invalid_thread_id); + + { + detail::coroutine_stackless_self self(this); + detail::reset_self_on_exit on_self_exit(&self, nullptr); + + reset_on_exit on_exit{*this}; + + HPX_UNUSED(on_exit); + + result = f_(arg); // invoke wrapped function + + // we always have to run to completion + HPX_ASSERT(result.first == threads::terminated); + + reset_tss(); + } + + reset(); + return result; + } + +}}} // namespace hpx::threads::coroutines + #endif diff --git a/libs/coroutines/src/detail/coroutine_impl.cpp b/libs/coroutines/src/detail/coroutine_impl.cpp index 739eb2c14c15..dc645e9e1617 100644 --- a/libs/coroutines/src/detail/coroutine_impl.cpp +++ b/libs/coroutines/src/detail/coroutine_impl.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -41,24 +41,6 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail { /////////////////////////////////////////////////////////////////////////// - namespace { - struct reset_self_on_exit - { - reset_self_on_exit( - coroutine_self* val, coroutine_self* old_val = nullptr) - : old_self(old_val) - { - coroutine_self::set_self(val); - } - - ~reset_self_on_exit() - { - coroutine_self::set_self(old_self); - } - - coroutine_self* old_self; - }; - } // namespace #if defined(HPX_DEBUG) coroutine_impl::~coroutine_impl() @@ -85,7 +67,7 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail { std::exception_ptr tinfo; { coroutine_self* old_self = coroutine_self::get_self(); - coroutine_self self(this, old_self); + coroutine_stackful_self self(this, old_self); reset_self_on_exit on_exit(&self, old_self); try { diff --git a/src/runtime/threads/thread_data.cpp b/src/runtime/threads/thread_data.cpp index d29aa620c23a..13cfcfc294ba 100644 --- a/src/runtime/threads/thread_data.cpp +++ b/src/runtime/threads/thread_data.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/tests/regressions/threads/CMakeLists.txt b/tests/regressions/threads/CMakeLists.txt index 4917b6121d9c..1e385664cfe5 100644 --- a/tests/regressions/threads/CMakeLists.txt +++ b/tests/regressions/threads/CMakeLists.txt @@ -10,6 +10,7 @@ set(tests resume_priority run_as_hpx_thread_exceptions_3304 run_as_os_thread_lockup_2991 + stackless_self_4155 thread_data_1111 thread_pool_executor_1112 thread_rescheduling diff --git a/tests/regressions/threads/stackless_self_4155.cpp b/tests/regressions/threads/stackless_self_4155.cpp new file mode 100644 index 000000000000..eb745af329be --- /dev/null +++ b/tests/regressions/threads/stackless_self_4155.cpp @@ -0,0 +1,21 @@ +// Copyright (C) 2019 Hartmut Kaiser +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +void stackless_thread() +{ + HPX_TEST_NEQ(hpx::threads::get_self_id(), hpx::threads::invalid_thread_id); +} + +int main(int argc, char* argv[]) +{ + hpx::threads::register_non_suspendable_work_nullary(stackless_thread); + return hpx::util::report_errors(); +} +