The design I want seems like fairly basic:
struct my_object {
explicit my_object(io_context& ctx)
: strand{make_strand(ctx)}
{
socket = make_unique<some_socket_like_object>(*strand);
}
void my_api() {
// Various async operations are launched through the socket
// which end up on the strand. They use some_data, this, etc.
}
~my_object() {
// finish all outstanding async operations that
// may access "some_data", "this" etc.
// delete the strand
strand.reset();
}
private:
// my class's data:
unique_ptr<socket> socket;
data some_data;
cancellation_signal some_cancellation;
// etc...
// strand will be deleted first
optional<strand<...>> strand;
};
What is a completely run-of-the mill behaviour in any async framework seems like an absolutely infeasible design with asio and I struggle to understand why it seems to fight so hard against the concept of encapsulation.
Even with doing things such as this in the dtor:
std::future<void> wait = boost::asio::dispatch(
boost::asio::bind_executor(
*m_strand
, std::packaged_task<void()>([] { /* cleanup */; })
)
);
wait.get();
and putting the strand in a std::optional
to control its deletion time explicitly, I still get tasks or completion handlers running in my io_context and referencing said strand after it and all the objects putting work on it were deleted:
~my_object() {
std::future<void> wait = boost::asio::dispatch(
boost::asio::bind_executor(
*m_strand
, std::packaged_task<void()>([this] {
// cleanup
socket.reset();
some_cancellation.emit(boost::asio::cancellation_type::all);
})
)
);
// Make sure that we get to a point where the socket object has been deleted and cannot submit new tasks ever:
wait.get();
// Make sure that the strand gets cleared
m_strand.reset();
// Here the io_context still has some tasks running that were put on the strand, accessing for instance !
}
e.g. later on:
#0 0x8010e64 in boost::asio::cancellation_slot::clear() /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/asio/impl/cancellation_signal.ipp:42:24
#1 0x8118d48 in void async_mqtt5::detail::cancellable_handler<boost::asio::detail::as_tuple_handler<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, std::tuple<boost::system::error_code, std::vector<async_mqtt5::reason_code, std::allocator<async_mqtt5:2
#2 0x8118d48 in async_mqtt5::detail::subscribe_op<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>, boost::asio::detail::as_tuple_handler<boost::asio::detail::awaitable_handler<boost:2
#3 0x8118238 in async_mqtt5::detail::subscribe_op<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>, boost::asio::detail::as_tuple_handler<boost::asio::detail::awaitable_handler<boost:1
#4 0x8117b44 in void boost::asio::detail::prepend_handler<async_mqtt5::detail::subscribe_op<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>, boost::asio::detail::as_tuple_handler<boo5
#5 0x8117b44 in void boost::asio::detail::prepend_handler<async_mqtt5::detail::subscribe_op<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>, boost::asio::detail::as_tuple_handler<boo1
#6 0x8117b44 in void boost::asio::detail::any_completion_handler_impl<boost::asio::detail::prepend_handler<async_mqtt5::detail::subscribe_op<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::mono5
#7 0x804d374 in boost::asio::detail::any_completion_handler_call_fn<void (boost::system::error_code)>::call(boost::asio::detail::any_completion_handler_impl_base*, boost::system::error_code) const /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/a5
#8 0x804d374 in decltype((*this).fn_table_->call((*this).impl_, static_cast<boost::system::error_code&>(fp))) boost::asio::any_completion_handler<void (boost::system::error_code)>::operator()<boost::system::error_code&>(boost::system::error_code&) /home/jcelerier/ossia/sc5
#9 0x8044fe8 in async_mqtt5::detail::write_req::complete(boost::system::error_code) /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/async-mqtt5/include/async_mqtt5/impl/async_sender.hpp:47:3
#10 0x8044fe8 in async_mqtt5::detail::async_sender<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>>::operator()(std::vector<async_mqtt5::detail::write_req, boost::asio::recycling_all7
#11 0x80449b8 in void std::__invoke_impl<void, async_mqtt5::detail::async_sender<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>>&, std::vector<async_mqtt5::detail::write_req, boost:4
#12 0x80439c0 in std::__invoke_result<async_mqtt5::detail::async_sender<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>>&, std::vector<async_mqtt5::detail::write_req, boost::asio::re4
#13 0x80439c0 in std::__invoke_result<async_mqtt5::detail::async_sender<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>>&, std::vector<async_mqtt5::detail::write_req, boost::asio::re1
#14 0x80439c0 in void boost::asio::detail::prepend_handler<std::reference_wrapper<async_mqtt5::detail::async_sender<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>>>, std::vector<asy5
#15 0x80439c0 in void boost::asio::detail::prepend_handler<std::reference_wrapper<async_mqtt5::detail::async_sender<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>>>, std::vector<asy1
#16 0x80439c0 in boost::asio::result_of<boost::asio::detail::prepend_handler<std::reference_wrapper<async_mqtt5::detail::async_sender<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, std::monostate>>2
#17 0x80439c0 in boost::asio::result_of<boost::asio::executor_binder<boost::asio::detail::prepend_handler<std::reference_wrapper<async_mqtt5::detail::async_sender<async_mqtt5::detail::client_service<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_i2
#18 0x80439c0 in async_mqtt5::detail::write_op<async_mqtt5::detail::autoconnect_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, async_mqtt5::detail::stream_context<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::a3
#19 0x80439c0 in async_mqtt5::detail::write_op<async_mqtt5::detail::autoconnect_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, async_mqtt5::detail::stream_context<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::ap
#20 0x80b6720 in void boost::asio::detail::prepend_handler<async_mqtt5::detail::write_op<async_mqtt5::detail::autoconnect_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, async_mqtt5::detail::stream_context<boost::asio::basic_st5
#21 0x80b6aa4 in void boost::asio::detail::prepend_handler<async_mqtt5::detail::write_op<async_mqtt5::detail::autoconnect_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, async_mqtt5::detail::stream_context<boost::asio::basic_st1
#22 0x80b6aa4 in boost::asio::detail::binder0<boost::asio::detail::prepend_handler<async_mqtt5::detail::write_op<async_mqtt5::detail::autoconnect_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, async_mqtt5::detail::stream_conte5
#23 0x80b6aa4 in void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::prepend_handler<async_mqtt5::detail::write_op<async_mqtt5::detail::autoconnect_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asi7
#24 0x786aa84 in boost::asio::detail::executor_function::operator()() /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/asio/detail/executor_function.hpp:61:7
#25 0x786aa84 in void boost::asio::detail::strand_executor_service::do_execute<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> const, boost::asio::detail::executor_function, std::allocator<void>>(std::shared_ptr<boost::asio::detail::strand_executor5
#26 0x786a700 in void boost::asio::detail::strand_executor_service::execute<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> const, boost::asio::detail::executor_function>(std::shared_ptr<boost::asio::detail::strand_executor_service::strand_impl> co0
#27 0x786a700 in boost::asio::constraint<traits::execute_member<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> const&, boost::asio::detail::executor_function>::is_valid, void>::type boost::asio::strand<boost::asio::io_context::basic_executor_type<5
#28 0x786a700 in void boost::asio::execution::detail::any_executor_base::execute_ex<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul>>>(boost::asio::execution::detail::any_executor_base const&, boost::asio::detail::executor_functi8
#29 0x80b5eac in void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::prepend_handler<async_mqtt5::detail::write_op<async_mqtt5::detail::autoconnect_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, 9
#30 0x80b5aac in boost::asio::detail::work_dispatcher<boost::asio::detail::prepend_handler<async_mqtt5::detail::write_op<async_mqtt5::detail::autoconnect_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, async_mqtt5::detail::stre5
#31 0x80b7160 in void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asio::detail::prepend_handler<async_mqtt5::detail::write_op<async_mqtt5::detail::autoconnect_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, bo7
#32 0x3a8a290 in boost::asio::detail::executor_function::operator()() /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/asio/detail/executor_function.hpp:61:7
#33 0x3a8a290 in boost::asio::detail::executor_op<boost::asio::detail::executor_function, std::allocator<void>, boost::asio::detail::scheduler_operation>::do_complete(void*, boost::asio::detail::scheduler_operation*, boost::system::error_code const&, unsigned long) /home/7
#34 0x7866a14 in boost::asio::detail::scheduler_operation::complete(void*, boost::system::error_code const&, unsigned long) /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/asio/detail/scheduler_operation.hpp:40:5
#35 0x7866a14 in boost::asio::detail::strand_executor_service::run_ready_handlers(std::shared_ptr<boost::asio::detail::strand_executor_service::strand_impl>&) /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/asio/detail/impl/strand_executor_servic8
#36 0x78680d0 in boost::asio::detail::strand_executor_service::invoker<boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul> const, void>::operator()() /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/asio/detail/impl/strand_exec5
#37 0x78680d0 in boost::asio::detail::executor_op<boost::asio::detail::strand_executor_service::invoker<boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul> const, void>, boost::asio::detail::recycling_allocator<void, boost::asio::detail::thread_info_ba7
#38 0x2b5ca68 in boost::asio::detail::scheduler_operation::complete(void*, boost::system::error_code const&, unsigned long) /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/asio/detail/scheduler_operation.hpp:40:5
#39 0x2b5ca68 in boost::asio::detail::scheduler::do_run_one(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&, boost::asio::detail::scheduler_thread_info&, boost::system::error_code const&) /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/b2
#40 0x2b5bdf8 in boost::asio::detail::scheduler::run(boost::system::error_code&) /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/asio/detail/impl/scheduler.ipp:210:10
#41 0x2b5f844 in boost::asio::io_context::run() /home/jcelerier/ossia/score/3rdparty/libossia/3rdparty/boost_1_85_0/boost/asio/impl/io_context.ipp:64:24
What must I do to make sure all the tasks are all completely processed and will never put any new task on the io_context at some point in my dtor ?