include/boost/capy/quitter.hpp

100.0% Lines (111 / 111, 1 excl) 90.3% Functions (130 / 144)
quitter.hpp
f(x) Functions (144)
Function Calls Lines Blocks
boost::capy::detail::quitter_return_base<int>::return_value(int) :61 9x 100.0% 100.0% boost::capy::detail::quitter_return_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::return_value(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) :61 1x 100.0% 100.0% boost::capy::detail::quitter_return_base<std::tuple<std::error_code, unsigned long> >::return_value(std::tuple<std::error_code, unsigned long>) :61 1x 100.0% 100.0% boost::capy::detail::quitter_return_base<int>::result() :66 4x 100.0% 100.0% boost::capy::detail::quitter_return_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::result() :66 1x 100.0% 100.0% boost::capy::detail::quitter_return_base<void>::return_void() :75 8x 100.0% 100.0% boost::capy::quitter<int>::promise_type::promise_type() :189 14x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::promise_type() :189 1x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::promise_type() :189 6x 100.0% 100.0% boost::capy::quitter<void>::promise_type::promise_type() :189 24x 100.0% 100.0% boost::capy::quitter<int>::promise_type::~promise_type() :195 14x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::~promise_type() :195 1x 80.0% 80.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::~promise_type() :195 6x 100.0% 100.0% boost::capy::quitter<void>::promise_type::~promise_type() :195 24x 100.0% 100.0% boost::capy::quitter<int>::promise_type::exception() const :214 10x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::exception() const :214 1x 80.0% 80.0% boost::capy::quitter<void>::promise_type::exception() const :214 27x 100.0% 100.0% boost::capy::quitter<int>::promise_type::stopped() const :228 5x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::stopped() const :228 6x 100.0% 100.0% boost::capy::quitter<void>::promise_type::stopped() const :228 1x 100.0% 100.0% boost::capy::quitter<int>::promise_type::get_return_object() :240 14x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::get_return_object() :240 1x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::get_return_object() :240 6x 100.0% 100.0% boost::capy::quitter<void>::promise_type::get_return_object() :240 24x 100.0% 100.0% boost::capy::quitter<int>::promise_type::initial_suspend() :257 14x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::initial_suspend() :257 1x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::initial_suspend() :257 6x 100.0% 100.0% boost::capy::quitter<void>::promise_type::initial_suspend() :257 24x 100.0% 100.0% boost::capy::quitter<int>::promise_type::final_suspend() :294 14x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::final_suspend() :294 1x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::final_suspend() :294 6x 100.0% 100.0% boost::capy::quitter<void>::promise_type::final_suspend() :294 21x 100.0% 100.0% boost::capy::quitter<int>::promise_type::unhandled_exception() :324 5x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::unhandled_exception() :324 0 0.0% 0.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::unhandled_exception() :324 5x 66.7% 73.0% boost::capy::quitter<void>::promise_type::unhandled_exception() :324 13x 100.0% 100.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<int> >::await_ready() :400 3x 66.7% 86.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter_test::bool_resume_awaitable>::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter_test::parked_until_stop>::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::yield_awaitable>::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::transform_awaiter<boost::capy::quitter_test::parked_until_stop>::await_ready() :400 5x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::async_mutex::lock_guard_awaiter>::await_ready() :400 1x 66.7% 71.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::async_waker::wait_awaiter>::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::park_awaitable>::await_ready() :400 0 0.0% 0.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_ready() :400 3x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::bool_park_awaitable>::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::parked_until_stop>::await_ready() :400 4x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::ready_results_awaitable>::await_ready() :400 3x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::rmw_ready_awaitable>::await_ready() :400 2x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::stop_then_return_self_awaitable>::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::stop_while_ready_awaitable>::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_ready() :400 0 0.0% 0.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::task<void> >::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_ready() :400 1x 66.7% 86.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<int> >::await_resume() :448 3x 87.5% 80.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_resume() :448 1x 87.5% 79.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter_test::bool_resume_awaitable>::await_resume() :448 1x 75.0% 57.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter_test::parked_until_stop>::await_resume() :448 1x 58.3% 41.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::yield_awaitable>::await_resume() :448 1x 87.5% 79.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::transform_awaiter<boost::capy::quitter_test::parked_until_stop>::await_resume() :448 5x 58.3% 41.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::async_mutex::lock_guard_awaiter>::await_resume() :448 1x 38.5% 25.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::async_waker::wait_awaiter>::await_resume() :448 1x 58.3% 41.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::park_awaitable>::await_resume() :448 0 0.0% 0.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_resume() :448 3x 87.5% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::bool_park_awaitable>::await_resume() :448 1x 87.5% 86.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::parked_until_stop>::await_resume() :448 4x 58.3% 41.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::ready_results_awaitable>::await_resume() :448 3x 84.6% 81.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::rmw_ready_awaitable>::await_resume() :448 2x 100.0% 100.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::stop_then_return_self_awaitable>::await_resume() :448 1x 75.0% 57.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::stop_while_ready_awaitable>::await_resume() :448 1x 75.0% 57.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_resume() :448 0 0.0% 0.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::task<void> >::await_resume() :448 0 0.0% 0.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_resume() :448 1x 66.7% 59.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_resume() :448 1x 66.7% 59.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<int> >::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :501 3x 80.0% 90.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :501 1x 80.0% 90.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter_test::bool_resume_awaitable>::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :501 1x 80.0% 86.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter_test::parked_until_stop>::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :501 1x 80.0% 90.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::yield_awaitable>::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :501 1x 80.0% 90.0% auto boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::transform_awaiter<boost::capy::quitter_test::parked_until_stop>::await_suspend<boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type>) :501 5x 80.0% 90.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::async_mutex::lock_guard_awaiter>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 0 0.0% 0.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::async_waker::wait_awaiter>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 1x 80.0% 90.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::park_awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 0 0.0% 0.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 3x 80.0% 90.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::bool_park_awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 1x 60.0% 86.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::parked_until_stop>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 4x 80.0% 90.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::ready_results_awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 0 0.0% 0.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::rmw_ready_awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 2x 80.0% 90.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::stop_then_return_self_awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter_test::stop_while_ready_awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 0 0.0% 0.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 0 0.0% 0.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::task<void> >::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 1x 80.0% 90.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 0 0.0% 0.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :501 1x 80.0% 86.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::quitter<int> >(boost::capy::quitter<int>&&) :571 3x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::quitter<void> >(boost::capy::quitter<void>&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::quitter_test::bool_resume_awaitable>(boost::capy::quitter_test::bool_resume_awaitable&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::quitter_test::parked_until_stop>(boost::capy::quitter_test::parked_until_stop&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::yield_awaitable>(boost::capy::yield_awaitable&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type::transform_awaitable<boost::capy::quitter_test::parked_until_stop>(boost::capy::quitter_test::parked_until_stop&&) :571 5x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::async_mutex::lock_guard_awaiter>(boost::capy::async_mutex::lock_guard_awaiter&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::async_waker::wait_awaiter>(boost::capy::async_waker::wait_awaiter&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::park_awaitable>(boost::capy::park_awaitable&&) :571 0 0.0% 0.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::quitter<void> >(boost::capy::quitter<void>&&) :571 3x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::quitter_test::bool_park_awaitable>(boost::capy::quitter_test::bool_park_awaitable&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::quitter_test::parked_until_stop>(boost::capy::quitter_test::parked_until_stop&&) :571 4x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::quitter_test::ready_results_awaitable>(boost::capy::quitter_test::ready_results_awaitable&&) :571 3x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::quitter_test::rmw_ready_awaitable>(boost::capy::quitter_test::rmw_ready_awaitable&&) :571 2x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::quitter_test::stop_then_return_self_awaitable>(boost::capy::quitter_test::stop_then_return_self_awaitable&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::quitter_test::stop_while_ready_awaitable>(boost::capy::quitter_test::stop_while_ready_awaitable&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::stop_only_awaitable>(boost::capy::stop_only_awaitable&&) :571 0 0.0% 0.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::task<void> >(boost::capy::task<void>&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>(boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable&&) :571 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>(boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable&&) :571 1x 100.0% 100.0% boost::capy::quitter<int>::~quitter() :604 26x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::~quitter() :604 2x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::~quitter() :604 24x 100.0% 100.0% boost::capy::quitter<void>::~quitter() :604 31x 100.0% 100.0% boost::capy::quitter<int>::await_ready() const :616 5x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::await_ready() const :616 6x 100.0% 100.0% boost::capy::quitter<void>::await_ready() const :616 5x 100.0% 100.0% boost::capy::quitter<int>::await_resume() :635 5x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::await_resume() :635 6x 85.7% 72.0% boost::capy::quitter<void>::await_resume() :635 1x 71.4% 53.0% boost::capy::quitter<int>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::await_context const*) :663 5x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::await_context const*) :663 6x 100.0% 100.0% boost::capy::quitter<void>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::await_context const*) :663 5x 100.0% 100.0% boost::capy::quitter<int>::handle() const :692 11x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::handle() const :692 1x 100.0% 100.0% boost::capy::quitter<void>::handle() const :692 17x 100.0% 100.0% boost::capy::quitter<int>::release() :709 9x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::release() :709 1x 100.0% 100.0% boost::capy::quitter<void>::release() :709 17x 100.0% 100.0% boost::capy::quitter<int>::quitter(boost::capy::quitter<int>&&) :735 12x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::quitter(boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&&) :735 1x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::quitter(boost::capy::quitter<std::tuple<std::error_code, unsigned long> >&&) :735 18x 100.0% 100.0% boost::capy::quitter<void>::quitter(boost::capy::quitter<void>&&) :735 7x 100.0% 100.0% boost::capy::quitter<void>::operator=(boost::capy::quitter<void>&&) :756 1x 100.0% 100.0% boost::capy::quitter<int>::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :770 14x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type>) :770 1x 100.0% 100.0% boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<std::tuple<std::error_code, unsigned long> >::promise_type>) :770 6x 100.0% 100.0% boost::capy::quitter<void>::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :770 24x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/capy
8 //
9
10 #ifndef BOOST_CAPY_QUITTER_HPP
11 #define BOOST_CAPY_QUITTER_HPP
12
13 #include <boost/capy/detail/config.hpp>
14 #include <boost/capy/detail/stop_requested_exception.hpp>
15 #include <boost/capy/cond.hpp>
16 #include <boost/capy/results.hpp>
17 #include <boost/capy/concept/executor.hpp>
18 #include <boost/capy/concept/context_awaitable.hpp>
19 #include <boost/capy/ex/context_awaitable_promise_base.hpp>
20 #include <boost/capy/ex/await_context.hpp>
21 #include <boost/capy/ex/frame_allocator.hpp>
22 #include <boost/capy/detail/await_suspend_helper.hpp>
23
24 #include <exception>
25 #include <optional>
26 #include <tuple>
27 #include <type_traits>
28 #include <utility>
29
30 /* Stop-aware coroutine task.
31
32 quitter<T> is identical to task<T> except that a stop request ends
33 the body without the body checking an error code. The promise's
34 transform_awaiter checks the stop token before it touches an awaited
35 operation, and again when an operation that completed
36 asynchronously resumes the body. A pending stop there throws a
37 sentinel exception that unwinds through RAII destructors to
38 final_suspend, and the parent sees a "stopped" completion. An
39 operation that completes synchronously always has its await_resume
40 called, because its await_ready or await_suspend may already have
41 acquired something. A results<> it returns holding cond::canceled
42 while the stop is pending acquired nothing, so the stop throws there;
43 any other result reaches the body, and the stop takes effect at the
44 next co_await.
45
46 See doc/quitter.md for the full design rationale. */
47
48 namespace boost {
49 namespace capy {
50
51 namespace detail {
52
53 // Reuse the same return-value storage as task<T>.
54 // task_return_base is defined in task.hpp, but quitter needs its own
55 // copy to avoid a header dependency on task.hpp.
56 template<typename T>
57 struct quitter_return_base
58 {
59 std::optional<T> result_;
60
61 11x void return_value(T value)
62 {
63 11x result_ = std::move(value);
64 11x }
65
66 5x T&& result() noexcept
67 {
68 5x return std::move(*result_);
69 }
70 };
71
72 template<>
73 struct quitter_return_base<void>
74 {
75 8x void return_void()
76 {
77 8x }
78 };
79
80 } // namespace detail
81
82 /** Defers a coroutine body until awaited, then unwinds it early on a stop request.
83
84 When the stop token is triggered, the body is unwound at a
85 `co_await` instead of resuming, and RAII destructors run normally.
86 Await-effects below says at which one. The parent observes a "stopped"
87 completion via @ref promise_type::stopped.
88
89 Everything else — frame allocation, environment propagation,
90 symmetric transfer, move semantics — is identical to @ref task.
91
92 @par Await-effects
93
94 Let `q` be a `quitter<T>`. `co_await q` always suspends the awaiting
95 coroutine, then transfers control directly into the quitter's
96 coroutine body on the current thread; no executor operation is
97 posted. The quitter records the caller's environment (executor, stop
98 token, and frame allocator) by pointer rather than copying it. It
99 propagates that environment to every `co_await` inside the body.
100
101 Unlike @ref task, the stop token is checked before the body's first
102 statement, before each awaited operation is started, and when an
103 operation that completed asynchronously resumes the body. If a stop
104 request is pending at those points, the body is not resumed. An
105 internal sentinel exception unwinds it instead, so RAII destructors
106 run, and the coroutine completes as stopped.
107
108 An awaited operation that completes synchronously always has its
109 `await_resume` called, even if a stop request arrives at the same
110 time. Its `await_ready` or `await_suspend` may already have acquired
111 a resource, such as `async_mutex`'s lock, that only its
112 `await_resume` hands over. If that result is a @ref results holding
113 `cond::canceled` while a stop is pending, the operation saw the stop
114 first and acquired nothing, so the body is unwound there. Any other
115 result is handed to the body, and the stop takes effect at the next
116 `co_await`. An operation whose result is not a @ref results can
117 therefore hand the body one result it produced after seeing the
118 stop.
119
120 The body runs until it returns, exits via an exception, or is unwound
121 by a stop request. Control then transfers directly back to the
122 awaiting coroutine, again without an executor operation.
123
124 @par Await-returns
125 The value the body passed to `co_return`, moved out of the quitter,
126 or nothing when `T` is `void`.
127
128 If the body exits via an unhandled exception, that exception is
129 rethrown instead.
130
131 If the coroutine completed as stopped, the internal sentinel
132 exception is thrown instead of await-returning. Awaiting a stopped
133 `quitter` from another `quitter` therefore stops that one too. A
134 @ref task awaiting it sees the sentinel as an unhandled exception in
135 its own body. When a quitter is started by `run_async`, a stopped
136 completion reaches the error handler as the sentinel
137 `std::exception_ptr`, not the value handler.
138
139 @par Await-postcondition
140 The quitter's coroutine has run to completion and is suspended at its
141 final suspend point; the body's RAII destructors have run. Exactly
142 one of the following holds: the body returned a value; the body
143 exited via an exception; or `handle().promise().stopped()` returns
144 `true`. When the body returned a value, the await moved it out, so a
145 quitter must not be awaited twice.
146
147 @par Remarks
148 Supports _ContextAwaitable cancellation_.
149
150 @tparam T The result type. Use `quitter<>` for `quitter<void>`.
151
152 @see @ref task, @ref ContextRunnable, @ref ContextAwaitable
153 */
154 template<typename T = void>
155 struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
156 quitter
157 {
158 /** Stores `quitter<T>`'s result and unwinds the body when the stop token fires.
159
160 This is the promise object the compiler associates with a
161 `quitter<T>` coroutine. It satisfies the coroutine promise
162 requirements and participates in the I/O awaitable protocol via
163 @ref context_awaitable_promise_base. Unlike @ref task::promise_type,
164 its `transform_awaitable` checks the stop token before each
165 awaited result reaches the body. A pending stop request throws an
166 internal sentinel exception that unwinds to a "stopped"
167 completion. It is part of the coroutine machinery and is not
168 intended to be used directly by callers.
169
170 Result storage and `return_value`/`return_void` are provided by
171 `detail::quitter_return_base<T>`.
172
173 @see @ref context_awaitable_promise_base, @ref ContextRunnable
174 */
175 struct promise_type
176 : context_awaitable_promise_base<promise_type>
177 , detail::quitter_return_base<T>
178 {
179 private:
180 friend quitter;
181
182 enum class completion { running, value, exception, stopped };
183
184 union { std::exception_ptr ep_; };
185 completion state_;
186
187 public:
188 /// Construct the promise in the running state.
189 45x promise_type() noexcept
190 45x : state_(completion::running)
191 {
192 45x }
193
194 /// Destroy the promise, releasing any stored exception.
195 45x ~promise_type()
196 {
197 45x if(state_ == completion::exception ||
198 41x state_ == completion::stopped)
199 23x ep_.~exception_ptr();
200 45x }
201
202 /** Return a non-null exception_ptr when the coroutine threw
203 or was stopped.
204
205 Stopped quitters report the sentinel
206 stop_requested_exception so that run_async routes to
207 the error handler instead of accessing a non-existent
208 result.
209
210 @return The stored exception if the coroutine exited via an
211 exception or was stopped, otherwise a null
212 `std::exception_ptr`.
213 */
214 38x std::exception_ptr exception() const noexcept
215 {
216 38x if(state_ == completion::exception ||
217 32x state_ == completion::stopped)
218 26x return ep_;
219 12x return {};
220 }
221
222 /** True when the coroutine was stopped via the stop token.
223
224 @return `true` if the body was unwound by a stop request;
225 `false` if it returned a value or exited via any other
226 exception.
227 */
228 12x bool stopped() const noexcept
229 {
230 12x return state_ == completion::stopped;
231 }
232
233 /** Return the owning `quitter` for this coroutine.
234
235 Called by the compiler to produce the object returned to the
236 caller when the coroutine is created.
237
238 @return A `quitter` owning the coroutine frame.
239 */
240 45x quitter get_return_object()
241 {
242 return quitter{
243 45x std::coroutine_handle<promise_type>::from_promise(*this)};
244 }
245
246 /** Return the initial-suspend awaiter.
247
248 The coroutine always suspends at the initial suspend point,
249 so the body does not start until the quitter is awaited. When
250 the body is resumed, the awaiter restores the thread-local
251 frame allocator. It then throws the internal sentinel
252 exception if stop is already requested, so the body never
253 runs and the coroutine completes as stopped.
254
255 @return An awaiter that suspends unconditionally.
256 */
257 45x auto initial_suspend() noexcept
258 {
259 struct awaiter
260 {
261 promise_type* p_;
262
263 bool await_ready() const noexcept
264 {
265 return false;
266 }
267
268 void await_suspend(std::coroutine_handle<>) const noexcept
269 {
270 }
271
272 // Potentially-throwing: checks the stop token before
273 // the coroutine body executes its first statement.
274 void await_resume() const
275 {
276 set_current_frame_allocator(
277 p_->environment()->frame_allocator);
278 if(p_->environment()->stop_token.stop_requested())
279 throw detail::stop_requested_exception{};
280 }
281 };
282 45x return awaiter{this};
283 }
284
285 /** Return the final-suspend awaiter.
286
287 The coroutine always suspends at the final suspend point. The
288 awaiter's `await_suspend` performs symmetric transfer to the
289 stored continuation, resuming the awaiting coroutine.
290
291 @return An awaiter that suspends and transfers to the
292 continuation.
293 */
294 42x auto final_suspend() noexcept
295 {
296 struct awaiter
297 {
298 promise_type* p_;
299
300 bool await_ready() const noexcept
301 {
302 return false;
303 }
304
305 std::coroutine_handle<> await_suspend(
306 std::coroutine_handle<>) const noexcept
307 {
308 return p_->continuation();
309 }
310
311 void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed
312 };
313 42x return awaiter{this};
314 }
315
316 /** Capture the in-flight exception from the coroutine body.
317
318 Called by the compiler when the coroutine body exits via an
319 unhandled exception. The internal stop sentinel is recorded as
320 a stopped completion; any other exception is recorded as an
321 exception completion. The stored exception is surfaced (or
322 routed to the error handler) when the quitter is awaited or run.
323 */
324 23x void unhandled_exception()
325 {
326 try
327 {
328 23x throw;
329 }
330 23x catch(detail::stop_requested_exception const&)
331 {
332 // Store the exception_ptr so that run_async's
333 // invoke_impl routes to the error handler
334 // instead of accessing a non-existent result.
335 19x new (&ep_) std::exception_ptr(
336 std::current_exception());
337 19x state_ = completion::stopped;
338 }
339 4x catch(...)
340 {
341 4x new (&ep_) std::exception_ptr(
342 std::current_exception());
343 4x state_ = completion::exception;
344 }
345 23x }
346
347 //------------------------------------------------------
348 // transform_awaitable — the key difference from task<T>
349 //------------------------------------------------------
350
351 /** Awaiter wrapping a nested `co_await` of an @ref ContextAwaitable.
352
353 Forwards the environment to the inner awaitable's
354 environment-taking `await_suspend` and restores the
355 thread-local frame allocator before the body resumes. Unlike
356 `task`'s, it also checks the stop token. A stop pending before
357 the wrapped awaitable is touched, or on resumption after an
358 asynchronous completion, throws the internal sentinel. The
359 body then unwinds before it observes the operation's result.
360 A synchronous completion is always handed over through the
361 wrapped awaitable's `await_resume`.
362
363 @tparam Awaitable The awaitable being transformed.
364 */
365 template<class Awaitable>
366 struct transform_awaiter
367 {
368 /// The wrapped awaitable, decayed and stored by value.
369 std::decay_t<Awaitable> a_;
370
371 /// The promise of the coroutine performing the `co_await`.
372 promise_type* p_;
373
374 /// True when a stop was pending at the `co_await`, so the
375 /// wrapped awaitable was never touched.
376 bool skipped_ = false;
377
378 /// True when the wrapped awaitable completed synchronously, so
379 /// its `await_resume` must run to hand over what it acquired.
380 bool completed_ = false;
381
382 /** Report whether the `co_await` can continue without
383 suspending.
384
385 The stop token is checked first. If a stop is pending, the
386 wrapped awaitable is never touched: this returns `true`, and
387 @ref await_resume throws. Otherwise the wrapped awaitable's
388 own `await_ready` decides. A `true` result there means it
389 completed synchronously, so @ref await_resume calls its
390 `await_resume` even if a stop arrives afterwards. That hands
391 over anything it acquired, such as `async_mutex`'s lock.
392
393 Where `BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND` is on,
394 this always returns `false`, and @ref await_suspend does both
395 steps instead. See `detail::await_ready_then_suspend`.
396
397 @return `true` if no suspension is needed. Always `false`
398 where the workaround is on.
399 */
400 32x bool await_ready() noexcept
401 {
402 #if BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND
403 return false;
404 #else
405 32x if(p_->environment()->stop_token.stop_requested())
406 {
407 1x skipped_ = true;
408 1x return true;
409 }
410 31x completed_ = a_.await_ready();
411 31x return completed_;
412 #endif
413 }
414
415 /** Restore the frame allocator, check for stop, then resume the
416 wrapped awaitable.
417
418 Reinstalls the thread-local frame allocator from the stored
419 environment. The internal sentinel exception is then thrown
420 in three cases: a stop was pending before the wrapped
421 awaitable was touched; a stop is pending now and the
422 wrapped awaitable completed asynchronously; or a stop is
423 pending now and the wrapped awaitable's result is a
424 @ref results holding `cond::canceled`. The body therefore
425 never observes that operation's result, and unwinds through
426 its RAII destructors to a stopped completion. A wrapped
427 awaitable that completed synchronously always has its
428 `await_resume` called, and any other result it gives is
429 returned, with the stop taking effect at the next
430 `co_await`. This is the one place `quitter` differs from
431 @ref task::promise_type::transform_awaiter.
432
433 After an asynchronous completion with a stop pending, the
434 operation's result is dropped. Releasing anything it held is
435 then the wrapped awaiter's destructor's job: `async_mutex`
436 releases a lock it granted, and `async_waker` drops the
437 wakeup. The body is unwinding, so nothing observes the loss.
438
439 @return The wrapped awaitable's await-result, forwarded
440 unchanged, when no sentinel is thrown.
441
442 @par Exception Safety
443 Throws the library's internal stop sentinel in the three
444 cases above. The wrapped awaitable's `await_resume` is not
445 called in the first two; in the third, its canceled result
446 is dropped.
447 */
448 31x decltype(auto) await_resume()
449 {
450 31x set_current_frame_allocator(
451 31x p_->environment()->frame_allocator);
452 61x if(skipped_ ||
453 52x (!completed_ &&
454 22x p_->environment()->stop_token.stop_requested()))
455 17x throw detail::stop_requested_exception{};
456 using R = decltype(a_.await_resume());
457 if constexpr (detail::is_results_v<R>)
458 {
459 // A result canceled by our own stop acquired
460 // nothing, so the stop can take effect here.
461 5x R r = a_.await_resume();
462 7x if(std::get<0>(r) == cond::canceled &&
463 2x p_->environment()->stop_token.stop_requested())
464 1x throw detail::stop_requested_exception{};
465 8x return r;
466 3x }
467 else
468 {
469 9x return a_.await_resume();
470 }
471 }
472
473 /** Suspend by calling the wrapped awaitable with the
474 environment.
475
476 This is the plain `await_suspend` the compiler calls for the
477 nested `co_await`. It forwards to the wrapped awaitable's
478 @ref ContextAwaitable overload, supplying the promise's stored
479 environment as the second argument. It keeps the wrapped
480 awaitable's suspension decision, whatever form it takes.
481 When that decision is to continue without suspending, the
482 wrapped awaitable completed synchronously, and
483 @ref await_resume will call its `await_resume`. Returning
484 `h` itself counts as continuing without suspending.
485
486 @param h The coroutine performing the `co_await`.
487
488 @return Whatever the wrapped awaitable's `await_suspend`
489 returns. When that is a `std::coroutine_handle<>`, the
490 handle is routed through `detail::symmetric_transfer`.
491 Where `BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND` is on,
492 this function instead returns `bool`. It first checks the
493 stop token, as @ref await_ready does where the workaround is
494 off, and returns `false` without touching the wrapped
495 awaitable if a stop is pending. Otherwise it returns the
496 result of `detail::await_ready_then_suspend`. Where the
497 workaround is off, a returned handle is passed back
498 unchanged for symmetric transfer.
499 */
500 template<class Promise>
501 26x auto await_suspend(
502 std::coroutine_handle<Promise> h) noexcept
503 {
504 // completed_ is written only where nothing else can have
505 // resumed this frame: a frame that really suspended may
506 // already be running, or gone, on another thread.
507 #if BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND
508 if(p_->environment()->stop_token.stop_requested())
509 {
510 skipped_ = true;
511 return false;
512 }
513 return detail::await_ready_then_suspend(
514 a_, h, p_->environment(), &completed_);
515 #else
516 using R = decltype(
517 a_.await_suspend(h, p_->environment()));
518 if constexpr (std::is_same_v<
519 R, std::coroutine_handle<>>)
520 {
521 std::coroutine_handle<> next =
522 23x a_.await_suspend(h, p_->environment());
523 // Returning h means no one else can resume this
524 // frame: a synchronous completion.
525 // GCC falsely warns that writing completed_ overruns the noop
526 // coroutine's frame: when the wrapped await_suspend returns
527 // std::noop_coroutine(), it assumes the branch below can be taken
528 // and treats this frame as the noop frame. A noop handle never
529 // equals h, so the branch is dead on that path.
530 #if defined(__GNUC__) && !defined(__clang__)
531 #pragma GCC diagnostic push
532 #pragma GCC diagnostic ignored "-Warray-bounds"
533 #endif
534 23x if(next == h)
535 1x completed_ = true;
536 #if defined(__GNUC__) && !defined(__clang__)
537 #pragma GCC diagnostic pop
538 #endif
539 23x return detail::symmetric_transfer(next);
540 }
541 else if constexpr (std::is_same_v<R, bool>)
542 {
543 3x if(!a_.await_suspend(h, p_->environment()))
544 {
545 2x completed_ = true;
546 2x return false;
547 }
548 1x return true;
549 }
550 else
551 {
552 return a_.await_suspend(
553 h, p_->environment());
554 }
555 #endif
556 }
557 };
558
559 /** Transform a nested awaitable before `co_await`.
560
561 Wraps an @ref ContextAwaitable in a @ref transform_awaiter so the
562 coroutine's environment is propagated into it and the stop
563 token is checked on resumption. A diagnostic is emitted if the
564 awaitable does not satisfy @ref ContextAwaitable.
565
566 @param a The awaitable expression from `co_await a`.
567
568 @return A @ref transform_awaiter wrapping `a`.
569 */
570 template<class Awaitable>
571 32x auto transform_awaitable(Awaitable&& a)
572 {
573 using A = std::decay_t<Awaitable>;
574 if constexpr (ContextAwaitable<A>)
575 {
576 return transform_awaiter<Awaitable>{
577 53x std::forward<Awaitable>(a), this};
578 }
579 else
580 {
581 static_assert(sizeof(A) == 0,
582 "requires ContextAwaitable");
583 }
584 21x }
585 };
586
587 /** Handle to the owned coroutine frame.
588
589 Null when the quitter is empty (for example after a move or after
590 @ref release). Prefer @ref handle to read this; the member is
591 public for use by the coroutine machinery.
592 */
593 std::coroutine_handle<promise_type> h_;
594
595 /** Destroy the quitter and its coroutine frame if owned.
596
597 @par Preconditions
598 The frame is not lent: no parent is suspended awaiting it,
599 unless its chain is being unwound after an @ref abandon.
600 Destroying a lent frame otherwise calls `std::terminate`; end
601 a running chain by requesting stop, or by destroying its
602 execution context.
603 */
604 83x ~quitter()
605 {
606 83x destroy_frame();
607 83x }
608
609 /** Return false; quitters are never immediately ready.
610
611 A quitter is lazy and has not started when it is awaited, so the
612 awaiting coroutine always suspends.
613
614 @return `false`.
615 */
616 16x bool await_ready() const noexcept
617 {
618 16x return false;
619 }
620
621 /** Return the result, rethrow exception, or propagate stop.
622
623 When stopped, throws stop_requested_exception so that a
624 parent quitter also stops. A parent task<T> sees this
625 as an unhandled exception — by design.
626
627 @return The result value for non-void `T`, moved out of the
628 quitter; otherwise `void`.
629
630 @par Exception Safety
631 If the coroutine was stopped, the library's internal stop sentinel
632 is thrown. If the body exited via any other exception, that
633 exception is rethrown.
634 */
635 12x auto await_resume()
636 {
637 12x lent_ = false;
638 12x if(h_.promise().stopped())
639 6x throw detail::stop_requested_exception{};
640 6x if(h_.promise().state_ == promise_type::completion::exception)
641 1x std::rethrow_exception(h_.promise().ep_);
642 if constexpr (! std::is_void_v<T>)
643 4x return std::move(*h_.promise().result_);
644 else
645 1x return;
646 }
647
648 /** Start execution with the caller's context.
649
650 Stores `cont` as the continuation to resume on completion.
651 Stores `env` as the execution environment propagated to nested
652 `co_await` expressions. Then transfers control into the quitter's
653 coroutine body via the returned handle.
654
655 @param cont The awaiting coroutine to resume when the quitter
656 completes.
657
658 @param env The execution environment (executor, stop token, and
659 frame allocator). It must outlive the quitter.
660
661 @return The quitter's coroutine handle, for symmetric transfer.
662 */
663 16x std::coroutine_handle<> await_suspend(
664 std::coroutine_handle<> cont,
665 await_context const* env)
666 {
667 16x h_.promise().set_continuation(cont);
668 16x h_.promise().set_environment(env);
669 16x lent_ = true;
670 16x return h_;
671 }
672
673 /** Return the coroutine handle.
674
675 @note Do not call `destroy()` on the returned handle while the
676 quitter is being awaited: the awaiting frame still holds it and
677 destroys it again, which has undefined behavior. A holder that
678 gives the coroutine up passes its continuation to @ref abandon
679 instead. (Destroying or
680 move-assigning over the quitter object itself while its frame is
681 lent is a different operation, and calls `std::terminate`.)
682 The quitter's lifetime is normally managed by `run_async`, `run`,
683 or the awaiting parent. For cooperative cancellation, use
684 `std::stop_token`.
685
686 @note The operation, queue or primitive holding a suspended
687 coroutine gives it up by passing the continuation it keeps to
688 @ref abandon. `handle()` belongs to the owning quitter object.
689
690 @return The coroutine handle.
691 */
692 29x std::coroutine_handle<promise_type> handle() const noexcept
693 {
694 29x return h_;
695 }
696
697 /** Release ownership of the coroutine frame.
698
699 @note The caller may call `destroy()` on the released handle
700 only when the quitter has not started or has fully completed.
701 Destroying the released handle of a quitter that is being
702 awaited is not supported and has undefined behavior.
703
704 @par Postconditions
705 `handle()` returns a null handle. Callers needing the
706 original handle must save it, via @ref handle, before
707 calling this.
708 */
709 27x void release() noexcept
710 {
711 27x h_ = nullptr;
712 27x }
713
714 /** Copy construction is disabled; a quitter uniquely owns its frame.
715
716 @param other The quitter that would be copied.
717 */
718 quitter(quitter const& other) = delete;
719
720 /** Copy assignment is disabled; a quitter uniquely owns its frame.
721
722 @param other The quitter that would be assigned from.
723
724 @return A reference to `*this`.
725 */
726 quitter& operator=(quitter const& other) = delete;
727
728 /** Construct by moving, transferring ownership.
729
730 @par Postconditions
731 `other` is empty and must not be awaited.
732
733 @param other The quitter to move from.
734 */
735 38x quitter(quitter&& other) noexcept
736 38x : h_(std::exchange(other.h_, nullptr))
737 {
738 38x }
739
740 /** Assign by moving, transferring ownership.
741
742 If this quitter already owns a coroutine frame, that frame is
743 destroyed first. Self-assignment is a no-op.
744
745 @par Preconditions
746 The frame being replaced is not lent: no parent is suspended
747 awaiting it. Replacing a lent frame calls `std::terminate`.
748
749 @par Postconditions
750 `other` is empty and must not be awaited.
751
752 @param other The quitter to move from.
753
754 @return A reference to `*this`.
755 */
756 1x quitter& operator=(quitter&& other) noexcept
757 {
758 1x if(this != &other)
759 {
760 1x destroy_frame();
761 1x h_ = std::exchange(other.h_, nullptr);
762 }
763 1x return *this;
764 }
765
766 private:
767 // Set while a parent is suspended awaiting this quitter.
768 bool lent_ = false;
769
770 45x explicit quitter(std::coroutine_handle<promise_type> h)
771 45x : h_(h)
772 {
773 45x }
774
775 84x void destroy_frame() noexcept
776 {
777 84x if(!h_)
778 66x return;
779 18x if(lent_ && !h_.done())
780 {
781 // Only the unwind of an abandoned chain destroys a frame
782 // that is still awaited.
783 1x auto* owner = h_.promise().environment()->owner;
784 1x if(!owner || !owner->abandoned())
785 − std::terminate(); // LCOV_EXCL_LINE reached only in a death test's child process
786 }
787 18x std::exchange(h_, nullptr).destroy();
788 18x lent_ = false;
789 }
790 };
791
792 } // namespace capy
793 } // namespace boost
794
795 #endif
796