mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-09-30 20:52:31 +07:00
Clear svr_sock_ before closing it on the accept loop's fatal path (#2560)
When accept() failed for a reason the retry branches do not cover, the loop closed svr_sock_ but left the descriptor in the atomic. Two things go wrong from there: - A later stop() reads the stale value and calls shutdown()/close() on it. By then the OS may have reused the descriptor for an unrelated socket (a worker's keep-alive connection, or one the application opened), and that connection is torn down instead. - keep_alive() in the worker threads watches svr_sock_ to notice that the server is going away, so the workers keep waiting on a listening socket that no longer exists. Take the descriptor with exchange(INVALID_SOCKET) before closing it, which is what stop() already does. That also settles the race with a concurrent stop(): whichever side takes the descriptor closes it exactly once, and the other sees INVALID_SOCKET and does nothing.
This commit is contained in:
@@ -13432,8 +13432,14 @@ inline bool Server::listen_internal() {
|
||||
} else if (errno == EINTR || errno == EAGAIN) {
|
||||
continue;
|
||||
}
|
||||
if (svr_sock_ != INVALID_SOCKET) {
|
||||
detail::close_socket(svr_sock_);
|
||||
// Take the descriptor out of svr_sock_ before closing it: a later
|
||||
// stop() would otherwise shutdown()/close() a value the OS may have
|
||||
// reused, and keep_alive() watches svr_sock_ to notice the server is
|
||||
// gone. The exchange also settles the race with a concurrent stop(),
|
||||
// since whichever side takes the descriptor closes it exactly once.
|
||||
auto listen_sock = svr_sock_.exchange(INVALID_SOCKET);
|
||||
if (listen_sock != INVALID_SOCKET) {
|
||||
detail::close_socket(listen_sock);
|
||||
ret = false;
|
||||
output_error_log(Error::Connection, nullptr);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user