diff --git a/include/ucoro/awaitable.hpp b/include/ucoro/awaitable.hpp index 92cca92..b69da56 100644 --- a/include/ucoro/awaitable.hpp +++ b/include/ucoro/awaitable.hpp @@ -55,12 +55,12 @@ namespace ucoro template struct await_transformer; - template - struct awaitable; - - template + template struct awaitable_promise; + template> + struct awaitable; + template struct CallbackAwaiter; @@ -143,27 +143,29 @@ namespace ucoro // template_parameter_of; // void // // 首先定义一个接受 template_parameter_of 这样的一个默认模板萃取 - template typename FromTemplate> + template typename FromTemplate> struct template_parameter_traits; // 接着定义一个偏特化,匹配 template_parameter_traits<模板名<参数>, 模板名> // 这样,这个偏特化的 template_parameter_traits 就有了一个 // 名为 template_parameter 的成员类型,其定义的类型就是 _template_parameter // 于是就把 TemplateParameter 这个类型给萃取出来了 - template typename ClassTemplate, typename TemplateParameter> - struct template_parameter_traits, ClassTemplate> + template typename ClassTemplate, typename... TemplateParameter> + struct template_parameter_traits, ClassTemplate> { - using template_parameter = TemplateParameter ; + using template_parameter_tuple = std::tuple; + using template_parameter = typename std::tuple_element<0, template_parameter_tuple>::type; }; // 最后,定义一个简化用法的 using 让用户的地方代码变短点 - template typename FromTemplate> + template typename FromTemplate> using template_parameter_of = typename template_parameter_traits< std::decay_t, FromTemplate>::template_parameter; // 利用 通用工具 template_parameter_of 萃取 local_storage_t 里的 T template using local_storage_value_type = template_parameter_of; + // 利用 通用工具 template_parameter_of 萃取 awaitable 里的 T @@ -188,28 +190,30 @@ namespace ucoro } // namespace traits - struct debug_coro_promise + template + struct coro_promise_allocation { -#if defined(DEBUG_CORO_PROMISE_LEAK) - void* operator new(std::size_t size) { - void* ptr = std::malloc(size); + void* ptr = Allocator{}.allocate(size);// std::malloc(size); if (!ptr) { throw std::bad_alloc{}; } +#if defined(DEBUG_CORO_PROMISE_LEAK) debug_coro_leak.insert(ptr); +#endif // DEBUG_CORO_PROMISE_LEAK return ptr; } void operator delete(void* ptr, [[maybe_unused]] std::size_t size) { +#if defined(DEBUG_CORO_PROMISE_LEAK) debug_coro_leak.erase(ptr); - std::free(ptr); +#endif // DEBUG_CORO_PROMISE_LEAK + Allocator{}.deallocate((typename Allocator::value_type*)(ptr), size); } -#endif // DEBUG_CORO_PROMISE_LEAK }; ////////////////////////////////////////////////////////////////////////// @@ -266,45 +270,42 @@ namespace ucoro } }; - ////////////////////////////////////////////////////////////////////////// - - template - struct final_awaitable : std::suspend_always - { - std::coroutine_handle<> await_suspend(std::coroutine_handle> h) noexcept - { - if (h.promise().continuation_) - { - // continuation_ 不为空,则 说明 .detach() 被 co_await - // 因此,awaitable_detached 析构的时候会顺便撤销自己,所以这里不用 destory - // 返回 continuation_,以便让协程框架调用 continuation_.resume() - // 这样就把等它的协程唤醒了. - return h.promise().continuation_; - } - // 并且,如果协程处于 .detach() 而没有被 co_await - // 则异常一直存储在 promise 里,并没有代码会去调用他的 await_resume() 重抛异常 - // 所以这里重新抛出来,避免有被静默吞并的异常 - h.promise().get_value(); - // 如果 continuation_ 为空,则说明 .detach() 没有被 co_await - // 因此,awaitable_detached 对象其实已经析构 - // 所以必须主动调用 destroy() 以免内存泄漏. - h.destroy(); - return std::noop_coroutine(); - } - }; - ////////////////////////////////////////////////////////////////////////// // 返回 T 的协程 awaitable_promise 实现. // Promise 类型实现... - template - struct awaitable_promise : public awaitable_promise_value, public debug_coro_promise + template + struct awaitable_promise : public awaitable_promise_value, public coro_promise_allocation { - awaitable get_return_object(); + awaitable get_return_object(); auto final_suspend() noexcept { - return final_awaitable{}; + struct final_awaitable : std::suspend_always + { + std::coroutine_handle<> await_suspend(std::coroutine_handle h) noexcept + { + if (h.promise().continuation_) + { + // continuation_ 不为空,则 说明 .detach() 被 co_await + // 因此,awaitable_detached 析构的时候会顺便撤销自己,所以这里不用 destory + // 返回 continuation_,以便让协程框架调用 continuation_.resume() + // 这样就把等它的协程唤醒了. + return h.promise().continuation_; + } + // 并且,如果协程处于 .detach() 而没有被 co_await + // 则异常一直存储在 promise 里,并没有代码会去调用他的 await_resume() 重抛异常 + // 所以这里重新抛出来,避免有被静默吞并的异常 + h.promise().get_value(); + // 如果 continuation_ 为空,则说明 .detach() 没有被 co_await + // 因此,awaitable_detached 对象其实已经析构 + // 所以必须主动调用 destroy() 以免内存泄漏. + h.destroy(); + return std::noop_coroutine(); + } + }; + + return final_awaitable{}; } auto initial_suspend() @@ -371,10 +372,10 @@ namespace ucoro ////////////////////////////////////////////////////////////////////////// // awaitable 协程包装... - template + template struct awaitable { - using promise_type = awaitable_promise; + using promise_type = awaitable_promise; explicit awaitable(std::coroutine_handle h) : current_coro_handle_(h) @@ -507,10 +508,10 @@ namespace ucoro ////////////////////////////////////////////////////////////////////////// - template - awaitable awaitable_promise::get_return_object() + template + awaitable awaitable_promise::get_return_object() { - auto result = awaitable{std::coroutine_handle>::from_promise(*this)}; + auto result = awaitable{std::coroutine_handle>::from_promise(*this)}; return result; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f5fc5d9..6ccf1b2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,3 +40,5 @@ if (LIBEVENT_FOUND) endif(LIBEVENT_FOUND) add_subdirectory(test_executor) + +add_subdirectory(test_custom_allocator) diff --git a/tests/test_custom_allocator/CMakeLists.txt b/tests/test_custom_allocator/CMakeLists.txt new file mode 100644 index 0000000..b5ef05c --- /dev/null +++ b/tests/test_custom_allocator/CMakeLists.txt @@ -0,0 +1,6 @@ + +add_executable(test_custom_allocator test.cpp) +target_link_libraries(test_custom_allocator ucoro) + +add_test(NAME test_custom_allocator COMMAND test_custom_allocator) +set_target_properties(test_custom_allocator PROPERTIES FOLDER "ucoro_tests") \ No newline at end of file diff --git a/tests/test_custom_allocator/test.cpp b/tests/test_custom_allocator/test.cpp new file mode 100644 index 0000000..661c459 --- /dev/null +++ b/tests/test_custom_allocator/test.cpp @@ -0,0 +1,63 @@ + +#include "ucoro/awaitable.hpp" +#include + +struct my_allocator +{ + using value_type = void; + + value_type* allocate(std::size_t size) + { + return std::malloc(size); + } + + void deallocate(value_type* ptr, std::size_t size) + { + free(ptr); + } + +}; + +// 只有 test2 用自定义分配器分配 +// 验证混合使用 分配器的awaitable也是没问题的 +ucoro::awaitable test() +{ + throw std::runtime_error("test throw"); + co_return 1; +} + + +ucoro::awaitable test2() +{ + throw std::runtime_error("test throw"); + co_return; +} + +ucoro::awaitable coro_compute() +{ + try + { + sync_await(test2()); + } + catch(const std::exception& e) + { + std::cerr << "exception in test2: " << e.what() << '\n'; + } + + co_return co_await test(); +} + +int main(int argc, char** argv) +{ + try + { + std::string str = "hello"; + sync_await(coro_compute(), str); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << std::endl; + } + + return 0; +}