From 92a1ca5917896e8c4525ebe22855c07ade466110 Mon Sep 17 00:00:00 2001 From: Xiaoguang Sun Date: Wed, 22 Apr 2015 17:51:28 +0800 Subject: [PATCH] Graceful shutdown and joinable server --- src/asio_io_service_pool.cc | 16 +++++++++------- src/asio_io_service_pool.h | 9 ++++++++- src/asio_server.cc | 14 ++++++++++++-- src/asio_server.h | 4 +++- src/asio_server_http2.cc | 20 ++++++++++++++++---- src/asio_server_http2_impl.cc | 15 ++++++++++++--- src/asio_server_http2_impl.h | 5 ++++- src/includes/nghttp2/asio_http2_server.h | 12 ++++++++++-- 8 files changed, 74 insertions(+), 21 deletions(-) diff --git a/src/asio_io_service_pool.cc b/src/asio_io_service_pool.cc index c6936d08..b6735df0 100644 --- a/src/asio_io_service_pool.cc +++ b/src/asio_io_service_pool.cc @@ -35,8 +35,6 @@ // #include "asio_io_service_pool.h" -#include - namespace nghttp2 { namespace asio_http2 { @@ -56,19 +54,23 @@ io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) { } } -void io_service_pool::run() { +void io_service_pool::run(bool asynchronous) { // Create a pool of threads to run all of the io_services. - auto futs = std::vector>(); - for (std::size_t i = 0; i < io_services_.size(); ++i) { - futs.push_back(std::async(std::launch::async, + futures_.push_back(std::async(std::launch::async, (size_t (boost::asio::io_service::*)(void)) & boost::asio::io_service::run, io_services_[i])); } + if (!asynchronous) { + join(); + } +} + +void io_service_pool::join() { // Wait for all threads in the pool to exit. - for (auto &fut : futs) { + for (auto &fut : futures_) { fut.get(); } } diff --git a/src/asio_io_service_pool.h b/src/asio_io_service_pool.h index 7ebd883c..242cac8f 100644 --- a/src/asio_io_service_pool.h +++ b/src/asio_io_service_pool.h @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -58,11 +59,14 @@ public: explicit io_service_pool(std::size_t pool_size); /// Run all io_service objects in the pool. - void run(); + void run(bool asynchronous = false); /// Stop all io_service objects in the pool. void stop(); + /// Join on all io_service objects in the pool. + void join(); + /// Get an io_service to use. boost::asio::io_service &get_io_service(); @@ -75,6 +79,9 @@ private: /// The next io_service to use for a connection. std::size_t next_io_service_; + + /// Futures to all the io_service objects + std::vector> futures_; }; } // namespace asio_http2 diff --git a/src/asio_server.cc b/src/asio_server.cc index 323a8991..1f10cd5f 100644 --- a/src/asio_server.cc +++ b/src/asio_server.cc @@ -50,7 +50,7 @@ boost::system::error_code server::listen_and_serve(boost::system::error_code &ec, boost::asio::ssl::context *tls_context, const std::string &address, const std::string &port, - int backlog, serve_mux &mux) { + int backlog, serve_mux &mux, bool asynchronous) { ec.clear(); if (bind_and_listen(ec, address, port, backlog)) { @@ -65,7 +65,7 @@ server::listen_and_serve(boost::system::error_code &ec, } } - io_service_pool_.run(); + io_service_pool_.run(asynchronous); return ec; } @@ -156,6 +156,16 @@ void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) { }); } +void server::stop() +{ + io_service_pool_.stop(); +} + +void server::join() +{ + io_service_pool_.join(); +} + } // namespace server } // namespace asio_http2 } // namespace nghttp2 diff --git a/src/asio_server.h b/src/asio_server.h index bb4159e9..7f601201 100644 --- a/src/asio_server.h +++ b/src/asio_server.h @@ -69,7 +69,9 @@ public: listen_and_serve(boost::system::error_code &ec, boost::asio::ssl::context *tls_context, const std::string &address, const std::string &port, - int backlog, serve_mux &mux); + int backlog, serve_mux &mux, bool asynchronous = false); + void join(); + void stop(); private: /// Initiate an asynchronous accept operation. diff --git a/src/asio_server_http2.cc b/src/asio_server_http2.cc index 92bc8898..85e50221 100644 --- a/src/asio_server_http2.cc +++ b/src/asio_server_http2.cc @@ -54,15 +54,17 @@ http2 &http2::operator=(http2 &&other) noexcept { boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec, const std::string &address, - const std::string &port) { - return impl_->listen_and_serve(ec, nullptr, address, port); + const std::string &port, + bool asynchronous) { + return impl_->listen_and_serve(ec, nullptr, address, port, asynchronous); } boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec, boost::asio::ssl::context &tls_context, - const std::string &address, const std::string &port) { - return impl_->listen_and_serve(ec, &tls_context, address, port); + const std::string &address, const std::string &port, + bool asynchronous) { + return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous); } void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); } @@ -73,6 +75,16 @@ bool http2::handle(std::string pattern, request_cb cb) { return impl_->handle(std::move(pattern), std::move(cb)); } +void http2::stop() +{ + impl_->stop(); +} + +void http2::join() +{ + return impl_->join(); +} + } // namespace server } // namespace asio_http2 diff --git a/src/asio_server_http2_impl.cc b/src/asio_server_http2_impl.cc index 96ca2432..142f418c 100644 --- a/src/asio_server_http2_impl.cc +++ b/src/asio_server_http2_impl.cc @@ -41,9 +41,9 @@ http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {} boost::system::error_code http2_impl::listen_and_serve( boost::system::error_code &ec, boost::asio::ssl::context *tls_context, - const std::string &address, const std::string &port) { - return server(num_threads_) - .listen_and_serve(ec, tls_context, address, port, backlog_, mux_); + const std::string &address, const std::string &port, bool asynchronous) { + server_.reset(new server(num_threads_)); + return server_->listen_and_serve(ec, tls_context, address, port, backlog_, mux_, asynchronous); } void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; } @@ -54,6 +54,15 @@ bool http2_impl::handle(std::string pattern, request_cb cb) { return mux_.handle(std::move(pattern), std::move(cb)); } +void http2_impl::stop() { + return server_->stop(); +} + +void http2_impl::join() +{ + return server_->join(); +} + } // namespace server } // namespace asio_http2 diff --git a/src/asio_server_http2_impl.h b/src/asio_server_http2_impl.h index f097aea0..244486fa 100644 --- a/src/asio_server_http2_impl.h +++ b/src/asio_server_http2_impl.h @@ -45,10 +45,13 @@ public: boost::system::error_code listen_and_serve(boost::system::error_code &ec, boost::asio::ssl::context *tls_context, - const std::string &address, const std::string &port); + const std::string &address, const std::string &port, + bool asynchronous); void num_threads(size_t num_threads); void backlog(int backlog); bool handle(std::string pattern, request_cb cb); + void stop(); + void join(); private: std::unique_ptr server_; diff --git a/src/includes/nghttp2/asio_http2_server.h b/src/includes/nghttp2/asio_http2_server.h index bef17336..b3386425 100644 --- a/src/includes/nghttp2/asio_http2_server.h +++ b/src/includes/nghttp2/asio_http2_server.h @@ -139,14 +139,16 @@ public: // incoming requests in cleartext TCP connection. boost::system::error_code listen_and_serve(boost::system::error_code &ec, const std::string &address, - const std::string &port); + const std::string &port, + bool asynchronous = false); // Starts listening connection on given address and port and serves // incoming requests in SSL/TLS encrypted connection. boost::system::error_code listen_and_serve(boost::system::error_code &ec, boost::asio::ssl::context &tls_context, - const std::string &address, const std::string &port); + const std::string &address, const std::string &port, + bool asynchronous = false); // Registers request handler |cb| with path pattern |pattern|. This // function will fail and returns false if same pattern has been @@ -187,6 +189,12 @@ public: // connections. void backlog(int backlog); + // Gracefully stop http2 server + void stop(); + + // Join on http2 server and wait for it to fully stop + void join(); + private: std::unique_ptr impl_; };