I have an ip::udp::socket
constructed with an io_service
. There is only one boost::thread
which calls the io_service::run()
method, and an instance of io_service::work
to prevent io_service::run()
from returning. The completion handlers for my ip::udp::socket
have custom asio_handler_allocate()
and asio_handler_deallocate()
functions, which are backed by a my::custom_memory_pool
.
When my application quits, this sequence of events occurs on on my shutting-down thread:
ip::udp::socket::close()
work::~work()
io_service::stop()
thread::join()
my::custom_memory_pool::~custom_memory_pool()
ip::udp::socket::~socket()
thread::~thread()
io_service::~io_service()
In step 8, the call to io_service::~io_service()
causes...
Program terminated with signal 11, Segmentation fault.
#0 0x00000000005ad93c in my::custom_memory_pool<boost::aligned_storage<512u, -1u> >::deallocate (this=0x36323f8, t=0x7fca97a07880)
at memory.hpp:82
82 reinterpret_cast<pool_node*>(t)->next_ = head_;
(gdb) bt 30
#0 0x00000000005ad93c in my::custom_memory_pool<boost::aligned_storage<512u, -1u> >::deallocate (this=0x36323f8, t=0x7fca97a07880)
at memory.hpp:82
#1 0x00000000005ad40a in asio_handler_deallocate (p=0x7fca97a07880, s=96, h=0x7fffe09d5480) at net.cpp:22
#2 0x0000000000571a07 in boost_asio_handler_alloc_helpers::deallocate<socket_multicast::completion_handler> (p=0x7fca97a07880, s=96, h=...)
at /usr/include/boost/asio/detail/handler_alloc_helpers.hpp:51
#3 0x0000000000558256 in boost::asio::detail::reactive_socket_recvfrom_op<boost::asio::mutable_buffers_1, boost::asio::ip::basic_endpoint<boost::asio::ip::udp>, socket_multicast::completion_handler>::ptr::reset (this=0x7fffe09d54b0)
at /usr/include/boost/asio/detail/reactive_socket_recvfrom_op.hpp:81
#4 0x0000000000558310 in boost::asio::detail::reactive_socket_recvfrom_op<boost::asio::mutable_buffers_1, boost::asio::ip::basic_endpoint<boost::asio::ip::udp>, socket_multicast::completion_handler>::do_complete (owner=0x0, base=0x7fca97a07880)
at /usr/include/boost/asio/detail/reactive_socket_recvfrom_op.hpp:112
#5 0x0000000000426706 in boost::asio::detail::task_io_service_operation::destroy (this=0x7fca97a07880)
at /usr/include/boost/asio/detail/task_io_service_operation.hpp:41
#6 0x000000000042841b in boost::asio::detail::task_io_service::shutdown_service (this=0xd4df30)
at /usr/include/boost/asio/detail/impl/task_io_service.ipp:96
#7 0x0000000000426388 in boost::asio::detail::service_registry::~service_registry (this=0xd4a320, __in_chrg=<value optimized out>)
at /usr/include/boost/asio/detail/impl/service_registry.ipp:43
#8 0x0000000000428e99 in boost::asio::io_service::~io_service (this=0xd49f38, __in_chrg=<value optimized out>)
at /usr/include/boost/asio/impl/io_service.ipp:51
So the io_service::~io_service()
is trying to deallocate some memory to the pool that I destroyed back in step 5.
I can't move my::custom_memory_pool::~custom_memory_pool()
to after io_service::~io_service()
.
I expected that after io_service::stop()
and thread::join()
returns, there could be no more asio_handler_deallocate()
calls. Apparently that's not the case. What can I do in step 3 to force io_service
to dequeue all of its completion events and deallocate all of its handler memory, and how can I block until io_service
finishes those tasks?
Asio defines boost::asio::io_service , a single class for an I/O service object. Every program based on Boost. Asio uses an object of type boost::asio::io_service . This can also be a global variable. While there is only one class for an I/O service object, several classes for I/O objects exist.
Stopping the io_service from running out of work boost::asio::io_service io_service; boost::asio::io_service::work work(io_service); ... To effect a shutdown, the application will then need to call the io_service object's stop() member function.
io_context::run runs until all scheduled tasks are completed. After that io_context::run will return and the caller thread will unblock: boost::asio::io_context io_context; // Schedule some tasks io_context.
Here is the answer: When tearing down an io_service
and its services, don't call io_service::stop()
at all. Just work::~work()
.
io_service::stop()
is really only for temporarily suspending the io_service
so that it may be io_service::reset()
later. An ordinary graceful shutdown of io_service
should not involve io_service::stop()
.
Calling io_service::stop()
doesn't allow it to finish any handlers, it simply stops processing after any currently running handler and returns as soon as possible.
Since the various internal structures are destroyed when the io_service
is destroyed, one solution is to control the order that the io_service is destroyed relative to the custom allocator. Either order them correctly if they're in the same structure (have the allocator prior to the io_service in the structure), or use the heap and explicitly order their destruction to guarantee that the io_service
is destroyed first.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With