Skip to content

Commit 7163caa

Browse files
committed
Fix GeventConnection close() race causing EBADF errors (#614)
GeventConnection.close() uses kill(block=False) which schedules GreenletExit for the next yield point, then closes the socket immediately. If close() is called from a non-gevent thread, the greenlet may still be mid-I/O when the socket is closed, causing EBADF. - Add is_closed/is_defunct guards in handle_read() and handle_write() error paths to silently exit during shutdown - Add GreenletExit exception handling in both I/O handlers for graceful greenlet termination (matching eventletreactor pattern) - Set last_error in close() when connected_event is not yet set to prevent factory() from returning a dead connection - Set last_error on server-initiated close (EOF) in handle_read()
1 parent f348637 commit 7163caa

1 file changed

Lines changed: 11 additions & 1 deletion

File tree

cassandra/io/geventreactor.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from gevent.queue import Queue
1717
from gevent import socket
1818
import gevent.ssl
19+
from greenlet import GreenletExit
1920

2021
import logging
2122
import time
@@ -98,7 +99,10 @@ def close(self):
9899
msg = "Connection to %s was closed" % self.endpoint
99100
if self.last_error:
100101
msg += ": %s" % (self.last_error,)
101-
self.error_all_requests(ConnectionShutdown(msg))
102+
shutdown_exc = ConnectionShutdown(msg)
103+
self.error_all_requests(shutdown_exc)
104+
if not self.connected_event.is_set():
105+
self.last_error = shutdown_exc
102106
# don't leave in-progress operations hanging
103107
self.connected_event.set()
104108

@@ -115,6 +119,8 @@ def handle_write(self):
115119
log.debug("Exception in send for %s: %s", self, err)
116120
self.defunct(err)
117121
return
122+
except GreenletExit:
123+
return
118124

119125
def handle_read(self):
120126
while True:
@@ -125,11 +131,15 @@ def handle_read(self):
125131
log.debug("Exception in read for %s: %s", self, err)
126132
self.defunct(err)
127133
return # leave the read loop
134+
except GreenletExit:
135+
return
128136

129137
if buf and self._iobuf.tell():
130138
self.process_io_buffer()
131139
else:
132140
log.debug("Connection %s closed by server", self)
141+
self.last_error = ConnectionShutdown(
142+
"Connection to %s was closed by server" % self.endpoint)
133143
self.close()
134144
return
135145

0 commit comments

Comments
 (0)