From ae1915271a3c726ee117baaee1d1fe720fceb3ab Mon Sep 17 00:00:00 2001 From: Israel Fruchter Date: Thu, 11 Jun 2026 17:49:35 +0300 Subject: [PATCH] fix: guard handle_read against closed socket race (#614) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add is_closed check at the top of LibevConnection.handle_read() to prevent recv() on an already-closed socket. Root cause: close() is called from any thread (main/app) and does _socket.close() synchronously, but the libev reactor thread may already be inside handle_read() for that connection (dispatched before _loop_will_run batches the watcher stop). Without this guard, recv() on the closed FD raises OSError [Errno 9] Bad file descriptor. This is the minimal fix for issue #614. The race is inherent in the threading model (single reactor thread shared across all sessions, close callable from any thread). The watcher stop is asynchronous (batched in _loop_will_run), but socket.close() is synchronous — this guard bridges the gap. Reproducer: scylladb/scylla-dtest#7079 --- cassandra/io/libevreactor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cassandra/io/libevreactor.py b/cassandra/io/libevreactor.py index 3da809931f..605840aa06 100644 --- a/cassandra/io/libevreactor.py +++ b/cassandra/io/libevreactor.py @@ -361,6 +361,8 @@ def handle_write(self, watcher, revents, errno=None): return def handle_read(self, watcher, revents, errno=None): + if self.is_closed: + return if revents & libev.EV_ERROR: if errno: exc = IOError(errno, os.strerror(errno))