diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d5a738..dc9d025 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,15 @@ target_sources(async_context PUBLIC FILES modules/async_context.cppm ) +target_compile_options(async_context PRIVATE + -g + -Werror + -Wno-unused-command-line-argument + -Wall + -Wextra + -Wshadow + -fexceptions + -fno-rtti) install( TARGETS async_context @@ -73,7 +82,15 @@ else() ) target_compile_features(async_unit_test PUBLIC cxx_std_23) - target_compile_options(async_unit_test PRIVATE -g) + target_compile_options(async_unit_test PRIVATE + -g + -Werror + -Wno-unused-command-line-argument + -Wall + -Wextra + -Wshadow + -fexceptions + -fno-rtti) target_link_libraries(async_unit_test PRIVATE async_context Boost::ut) add_custom_target(run_tests ALL DEPENDS async_unit_test COMMAND async_unit_test) diff --git a/conanfile.py b/conanfile.py index ec432ef..0936728 100644 --- a/conanfile.py +++ b/conanfile.py @@ -89,7 +89,7 @@ def build_requirements(self): self.test_requires("boost-ext-ut/2.3.1") def requirements(self): - self.requires("strong_ptr/0.0.2") + self.requires("strong_ptr/0.1.2") def layout(self): cmake_layout(self) diff --git a/modules/async_context.cppm b/modules/async_context.cppm index dd75104..5b143c3 100644 --- a/modules/async_context.cppm +++ b/modules/async_context.cppm @@ -147,11 +147,29 @@ public: return do_schedule(p_context, p_block_state, p_block_info); } + /** + * @brief Get allocator from scheduler + * + * The memory_resource returned be owned or embedded within the scheduler. The + * memory_resource and its backing memory must live as long as the scheduler. + * The returned reference MUST NOT be bound to a nullptr. + * + * @return std::pmr::memory_resource& - the memory resource to be used to + * allocate memory for async::context stack memory. The memory_resource must + * be owned or embedded within the scheduler. + */ + std::pmr::memory_resource& get_allocator() noexcept + { + return do_get_allocator(); + } + private: virtual void do_schedule( context& p_context, blocked_by p_block_state, std::variant p_block_info) = 0; + + virtual std::pmr::memory_resource& do_get_allocator() noexcept = 0; }; export class context @@ -159,22 +177,23 @@ export class context public: static auto constexpr default_timeout = sleep_duration(0); - // TODO(#18): Replace `mem::strong_ptr>` stack memory type // with something thats easier and safer to work with. /** * @brief Construct a new context object * * @param p_scheduler - a pointer to a transition handler that * handles transitions in blocked_by state. - * @param p_stack_memory - span to a block of memory reserved for this context - * to be used as stack memory for coroutine persistent memory. This buffer - * must outlive the lifetime of this object. + * @param p_stack_size - Number of bytes to allocate for the context's stack + * memory. */ - context(mem::strong_ptr const& p_scheduler, - mem::strong_ptr> const& p_stack_memory) + context(mem::strong_ptr const& p_scheduler, usize p_stack_size) : m_scheduler(p_scheduler) - , m_stack(p_stack_memory) { + using poly_allocator = std::pmr::polymorphic_allocator; + auto allocator = poly_allocator(&p_scheduler->get_allocator()); + + // Allocate memory for stack and assign to m_stack + m_stack = { allocator.allocate_object(p_stack_size), p_stack_size }; } void unblock() @@ -227,7 +246,7 @@ public: constexpr auto capacity() { - return m_stack->size(); + return m_stack.size(); } constexpr auto memory_remaining() @@ -258,11 +277,11 @@ public: [[nodiscard]] constexpr void* allocate(std::size_t p_bytes) { auto const new_stack_index = m_stack_index + p_bytes; - if (new_stack_index > m_stack->size()) [[unlikely]] { + if (new_stack_index > m_stack.size()) [[unlikely]] { throw bad_coroutine_alloc(this); } m_state = p_bytes; - auto* const stack_address = &(*m_stack)[m_stack_index]; + auto* const stack_address = &m_stack[m_stack_index]; m_stack_index = new_stack_index; return stack_address; } @@ -277,15 +296,23 @@ public: m_state = p_exception; } + ~context() + { + using poly_allocator = std::pmr::polymorphic_allocator; + auto allocator = poly_allocator(&m_scheduler->get_allocator()); + allocator.deallocate_object(m_stack.data(), m_stack.size()); + }; + private: friend class promise_base; + using context_state = std::variant; // Should stay within a standard cache-line of 64 bytes (8 words) - std::coroutine_handle<> m_active_handle = std::noop_coroutine(); // word 1 - mem::strong_ptr m_scheduler; // word 2-3 - mem::strong_ptr> m_stack; // word 4-5 - usize m_stack_index = 0; // word 6 - std::variant m_state; // word 7-8 + mem::strong_ptr m_scheduler; // word 1-2 + std::coroutine_handle<> m_active_handle = std::noop_coroutine(); // word 3 + std::span m_stack{}; // word 4-5 + usize m_stack_index{ 0uz }; // word 6 + context_state m_state{ 0uz }; // word 7-8 }; static_assert(sizeof(context) <= diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index d544a50..ee0c39e 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -36,6 +36,15 @@ target_sources(${PROJECT_NAME} PUBLIC target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_23) target_link_libraries(${PROJECT_NAME} PRIVATE async_context) +target_compile_options(${PROJECT_NAME} PRIVATE + -g + -Werror + -Wno-unused-command-line-argument + -Wall + -Wextra + -Wshadow + -fexceptions + -fno-rtti) # Always run this custom target by making it depend on ALL add_custom_target(copy_compile_commands ALL COMMAND ${CMAKE_COMMAND} -E copy_if_different diff --git a/test_package/main.cpp b/test_package/main.cpp index 92d7052..408bc71 100644 --- a/test_package/main.cpp +++ b/test_package/main.cpp @@ -14,20 +14,24 @@ #include -#include #include #include #include #include -#include #include import async_context; -struct my_scheduler : public async::scheduler +struct my_scheduler + : public async::scheduler + , mem::enable_strong_from_this { int sleep_count = 0; + my_scheduler(mem::strong_ptr_only_token) + { + } + private: void do_schedule( [[maybe_unused]] async::context& p_context, @@ -39,6 +43,11 @@ struct my_scheduler : public async::scheduler sleep_count++; } } + + std::pmr::memory_resource& do_get_allocator() noexcept override + { + return *strong_from_this().get_allocator(); + } }; async::future coro_double_delay(async::context&) @@ -56,11 +65,7 @@ int main() { auto scheduler = mem::make_strong_ptr(std::pmr::new_delete_resource()); - auto buffer = mem::make_strong_ptr>( - std::pmr::new_delete_resource()); - auto buffer_span = mem::make_strong_ptr>( - std::pmr::new_delete_resource(), *buffer); - async::context my_context(scheduler, buffer_span); + async::context my_context(scheduler, 1024); auto future_delay = coro_double_delay(my_context); diff --git a/tests/async.test.cpp b/tests/async.test.cpp index 4722b41..e47efe0 100644 --- a/tests/async.test.cpp +++ b/tests/async.test.cpp @@ -40,10 +40,16 @@ boost::ut::suite<"async::context"> async_context_suite = []() { using namespace boost::ut; ""_test = []() { - struct my_scheduler : public async::scheduler + struct my_scheduler + : public async::scheduler + , mem::enable_strong_from_this { int sleep_count = 0; + my_scheduler(mem::strong_ptr_only_token) + { + } + private: void do_schedule( [[maybe_unused]] context& p_context, @@ -59,15 +65,16 @@ boost::ut::suite<"async::context"> async_context_suite = []() { std::println("Sleep count = {}!", sleep_count); } } + + std::pmr::memory_resource& do_get_allocator() noexcept override + { + return *strong_from_this().get_allocator(); + } }; // Setup auto scheduler = mem::make_strong_ptr(std::pmr::new_delete_resource()); - auto buffer = mem::make_strong_ptr>( - std::pmr::new_delete_resource()); - auto buffer_span = mem::make_strong_ptr>( - std::pmr::new_delete_resource(), *buffer); - async::context my_context(scheduler, buffer_span); + async::context my_context(scheduler, 1024); // Exercise auto future_print = coro_print(my_context);