Add promise to wait for closure of server socket
This may fix the issues that a websocket server may crash or hang when quitting
This commit is contained in:
parent
9ae294293f
commit
aa671d61b5
1 changed files with 48 additions and 31 deletions
|
|
@ -322,66 +322,82 @@ namespace fc { namespace http {
|
|||
{
|
||||
_connections[hdl]->closed();
|
||||
_connections.erase( hdl );
|
||||
if( _connections.empty() && _all_connections_closed )
|
||||
_all_connections_closed->set_value();
|
||||
}
|
||||
else
|
||||
{
|
||||
wlog( "unknown connection closed" );
|
||||
}
|
||||
if( _connections.empty() && _closed )
|
||||
_closed->set_value();
|
||||
}).wait();
|
||||
});
|
||||
|
||||
_server.set_fail_handler( [this]( connection_hdl hdl ){
|
||||
if( _server.is_listening() )
|
||||
{
|
||||
_server_thread.async( [this,hdl](){
|
||||
if( _connections.find(hdl) != _connections.end() )
|
||||
{
|
||||
_connections[hdl]->closed();
|
||||
_connections.erase( hdl );
|
||||
}
|
||||
_server_thread.async( [this,hdl](){
|
||||
if( _connections.find(hdl) != _connections.end() )
|
||||
{
|
||||
_connections[hdl]->closed();
|
||||
_connections.erase( hdl );
|
||||
if( _connections.empty() && _all_connections_closed )
|
||||
_all_connections_closed->set_value();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the server is shutting down, assume this hdl is the server socket
|
||||
if( _server_socket_closed )
|
||||
_server_socket_closed->set_value();
|
||||
else
|
||||
{
|
||||
wlog( "unknown connection failed" );
|
||||
}
|
||||
if( _connections.empty() && _closed )
|
||||
_closed->set_value();
|
||||
}).wait();
|
||||
}
|
||||
}
|
||||
}).wait();
|
||||
});
|
||||
}
|
||||
|
||||
virtual ~generic_websocket_server_impl()
|
||||
{
|
||||
if( _server.is_listening() )
|
||||
{
|
||||
// _server.stop_listening() may trigger the on_fail callback function (the lambda function set by
|
||||
// _server.set_fail_handler(...) ) for the listening server socket (note: the connection handle
|
||||
// associated with the server socket is not in our connection map),
|
||||
// the on_fail callback function may fire (async) a new task which may run really late
|
||||
// and it will try to access the member variables of this server object,
|
||||
// so we need to wait for it before destructing this object.
|
||||
_server_socket_closed = promise<void>::create();
|
||||
_server.stop_listening();
|
||||
}
|
||||
|
||||
// Note: since _connections can be modified by lambda functions in set_*_handler, which are running
|
||||
// in another thread, perhaps we need to wait for them (especially the one in set_open_handler)
|
||||
// being processed. Otherwise `_closed.wait()` may hang.
|
||||
if( _connections.size() )
|
||||
// in other tasks, perhaps we need to wait for them (especially the one in set_open_handler)
|
||||
// being processed. Otherwise `_all_connections_closed.wait()` may hang.
|
||||
if( !_connections.empty() )
|
||||
{
|
||||
_closed = promise<void>::create();
|
||||
_all_connections_closed = promise<void>::create();
|
||||
|
||||
auto cpy_con = _connections;
|
||||
websocketpp::lib::error_code ec;
|
||||
for( auto& item : cpy_con )
|
||||
_server.close( item.first, 0, "server exit" );
|
||||
_server.close( item.first, 0, "server exit", ec );
|
||||
|
||||
_closed->wait();
|
||||
_all_connections_closed->wait();
|
||||
}
|
||||
|
||||
if( _server_socket_closed )
|
||||
_server_socket_closed->wait();
|
||||
}
|
||||
|
||||
typedef std::map<connection_hdl, websocket_connection_ptr, std::owner_less<connection_hdl> > con_map;
|
||||
|
||||
con_map _connections; // Note: std::map is not thread-safe nor task-safe, we may need
|
||||
// to use a mutex or similar to avoid concurrent access.
|
||||
fc::thread& _server_thread;
|
||||
websocketpp::server<T> _server;
|
||||
on_connection_handler _on_connection;
|
||||
fc::promise<void>::ptr _closed;
|
||||
uint32_t _pending_messages = 0;
|
||||
std::string _forward_header_key; // A header like "X-Forwarded-For" (XFF), with data IP:port
|
||||
// Note: std::map is not thread-safe nor task-safe, we may need
|
||||
// to use a mutex or similar to avoid concurrent access.
|
||||
con_map _connections; ///< Holds accepted connections
|
||||
fc::thread& _server_thread; ///< The thread that runs the server
|
||||
websocketpp::server<T> _server; ///< The server
|
||||
on_connection_handler _on_connection; ///< A handler to be called when a new connection is accepted
|
||||
fc::promise<void>::ptr _all_connections_closed; ///< Promise to wait for all connections to be closed
|
||||
fc::promise<void>::ptr _server_socket_closed; ///< Promise to wait for the server socket to be closed
|
||||
uint32_t _pending_messages = 0; ///< Number of messages not processed, for rate limiting
|
||||
std::string _forward_header_key; ///< A header like "X-Forwarded-For" (XFF) with data IP:port
|
||||
};
|
||||
|
||||
class websocket_server_impl : public generic_websocket_server_impl<asio_with_stub_log>
|
||||
|
|
@ -649,8 +665,9 @@ namespace fc { namespace http {
|
|||
|
||||
void websocket_server::close()
|
||||
{
|
||||
websocketpp::lib::error_code ec;
|
||||
for( auto& connection : my->_connections )
|
||||
my->_server.close( connection.first, websocketpp::close::status::normal, "Goodbye" );
|
||||
my->_server.close( connection.first, websocketpp::close::status::normal, "Goodbye", ec );
|
||||
}
|
||||
|
||||
websocket_tls_server::websocket_tls_server( const string& server_pem, const string& ssl_password,
|
||||
|
|
|
|||
Loading…
Reference in a new issue