5

I'm working with one of the example http servers that comes with boost (at doc/html/boost_asio/example/cpp03/http/server, or alternately at http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/examples/cpp03_examples.html#boost_asio.examples.cpp03_examples.http_server).

The example opens an acceptor socket and listens, peeling off requests to request objects. The server also has a boost asio signal_set that registers signal handlers and cancels all asio requests from within the io_service when SIGINT or SIGTERM are received. Press CTRL-C and the signal is received, the io_service calls out to a handle_stop() function, which then calls .close() on all the asio::ip::tcp::sockets. The whole server shuts down gracefully, the io_service.run() call exits, and the program ends.

I'd like to be able to start up this http server in a thread, and then cancel it programmatically rather than using signals. What's the accepted way of doing this? I removed the signal_set and the signal handlers, and then started the server on another thread. It handles http requests well enough from that new thread. How do I stop it from another thread? Is it at all safe to call asio::ip::tcp::socket::close() from a different thread? The documentation is unclear, and just doing that feels very, un-asio-ish. Indeed, when I try to do this, it works well as long as I haven't accepted any http requests yet. If I've processed even a single http request, the process crashes deep in boost:

>   test_ssl_server_sa.exe!boost::detail::sp_counted_base::add_ref_lock()  Line 81 + 0x3 bytes  C++
    test_ssl_server_sa.exe!boost::detail::shared_count::shared_count(const boost::detail::weak_count & r={...})  Line 578 + 0x12 bytes  C++
    test_ssl_server_sa.exe!boost::shared_ptr<http::server::connection>::shared_ptr<http::server::connection><http::server::connection>(const boost::weak_ptr<http::server::connection> & r={...})  Line 405 + 0x3f bytes    C++
    test_ssl_server_sa.exe!boost::enable_shared_from_this<http::server::connection>::shared_from_this()  Line 49 + 0xc bytes    C++
    test_ssl_server_sa.exe!http::server::connection::handle_handshake(const boost::system::error_code & error={...})  Line 83 + 0x11 bytes  C++
    test_ssl_server_sa.exe!boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>::operator()(http::server::connection * p=0x004b8fe8, const boost::system::error_code & a1={...})  Line 165 + 0x18 bytes    C++
    test_ssl_server_sa.exe!boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> >::operator()<boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list1<boost::system::error_code const &> >(boost::_bi::type<void> __formal={...}, boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &> & f={...}, boost::_bi::list1<boost::system::error_code const &> & a={...}, boost::_bi::type<void> __formal={...})  Line 314    C++
    test_ssl_server_sa.exe!boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > >::operator()<boost::system::error_code>(const boost::system::error_code & a1={...})  Line 48  C++
    test_ssl_server_sa.exe!boost::asio::ssl::detail::handshake_op::call_handler<boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >(boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > & handler={...}, const boost::system::error_code & ec={...}, const unsigned int & __formal=0)  Line 55    C++
    test_ssl_server_sa.exe!boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >::operator()(boost::system::error_code ec={...}, unsigned int bytes_transferred=0, int start=0)  Line 276   C++
    test_ssl_server_sa.exe!boost::asio::detail::binder2<boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >,boost::system::error_code,unsigned int>::operator()()  Line 129   C++
    test_ssl_server_sa.exe!boost::asio::asio_handler_invoke<boost::asio::detail::binder2<boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >,boost::system::error_code,unsigned int> >(boost::asio::detail::binder2<boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >,boost::system::error_code,unsigned int> & function={...}, ...)  Line 70 C++
    test_ssl_server_sa.exe!boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::binder2<boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >,boost::system::error_code,unsigned int>,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >(boost::asio::detail::binder2<boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >,boost::system::error_code,unsigned int> & function={...}, boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > & context={...})  Line 37 + 0x16 bytes   C++
    test_ssl_server_sa.exe!boost::asio::ssl::detail::asio_handler_invoke<boost::asio::detail::binder2<boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >,boost::system::error_code,unsigned int>,boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >(boost::asio::detail::binder2<boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >,boost::system::error_code,unsigned int> & function={...}, boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > > * this_handler=0x00fefb70)  Line 319 + 0x10 bytes  C++
    test_ssl_server_sa.exe!boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::binder2<boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >,boost::system::error_code,unsigned int>,boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > > >(boost::asio::detail::binder2<boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > >,boost::system::error_code,unsigned int> & function={...}, boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > > & context={...})  Line 37 + 0x16 bytes   C++
    test_ssl_server_sa.exe!boost::asio::detail::reactive_socket_recv_op<boost::asio::mutable_buffers_1,boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ssl::detail::handshake_op,boost::_bi::bind_t<void,boost::_mfi::mf1<void,http::server::connection,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<http::server::connection *>,boost::arg<1> > > > >::do_complete(boost::asio::detail::task_io_service * owner=0x00c1aca0, boost::asio::detail::task_io_service_operation * base=0x00c1f948, const boost::system::error_code & __formal={...}, const boost::system::error_code & __formal={...})  Line 110 + 0xd bytes    C++
    test_ssl_server_sa.exe!boost::asio::detail::task_io_service_operation::complete(boost::asio::detail::task_io_service & owner={...}, const boost::system::error_code & ec={...}, unsigned int bytes_transferred=0)  Line 38 + 0x1a bytes C++
    test_ssl_server_sa.exe!boost::asio::detail::task_io_service::do_run_one(boost::asio::detail::scoped_lock<boost::asio::detail::win_mutex> & lock={...}, boost::asio::detail::task_io_service_thread_info & this_thread={...}, const boost::system::error_code & ec={...})  Line 386  C++
    test_ssl_server_sa.exe!boost::asio::detail::task_io_service::run(boost::system::error_code & ec={...})  Line 153 + 0x1e bytes   C++

I'm not saying that the server might not have a different synchronization problem - in particular, it looks like the server is crashing with multiple access on the boost::shared_ptr to the connection object. But I'm more interested in what the right way of shutting down an asio io_service.run() call from another thread would be. When I imagine how I'd like this to work, I imagine an io_service request that waits on a control variable. If I wanted to shut down the server, I would signal the control variable from another thread, and the io_service would call my callback on the io_service thread. From there, it could .close() any sockets or otherwise cancel any other in-flight requests.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
Ted Middleton
  • 6,859
  • 10
  • 51
  • 71
  • 3
    do I see a single type there with 1696 characters? how charming. – Karoly Horvath Aug 17 '14 at 07:50
  • Boost ASIO sockets are not thread-safe so you need to ensure that there is no access concurrent with closing. See http://stackoverflow.com/a/21503938/1462337. I suspect your crash is a different issue, though, as your description makes it sound perfectly reproducible. – rhashimoto Aug 17 '14 at 17:43
  • 1
    Hard to identify the exact problem without code available. However, a graceful shutdown can be accomplished by calling [`io_service::close()`](http://www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/io_service/stop.html) then joining all threads that were running the `io_service`. If the sockets are managed directly or indirectly through `shared_ptr`s bound within handlers, then they will be [closed and destroyed](http://stackoverflow.com/a/21716271/1053968) during the `io_service`'s destruction. – Tanner Sansbury Aug 18 '14 at 03:36

1 Answers1

0

You could use boost::asio::io_service::post with a lambda from the thread trying to stop the http server. The lambda will be executed in the http server thread. It can safely call socket::close on all sockets. This will gracefully stop the service.

David Feurle
  • 2,687
  • 22
  • 38