Skip to content

Commit 8ebd553

Browse files
committed
PYTHON-799 Avoid deadlock in Cursor destructor with PyPy.
1 parent e93c2ac commit 8ebd553

File tree

2 files changed

+25
-12
lines changed

2 files changed

+25
-12
lines changed

pymongo/mongo_client.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ def __check_bson_size(self, message):
10961096
return message
10971097

10981098
def _send_message(self, message,
1099-
with_last_error=False, command=False, check_primary=True):
1099+
with_last_error=False, command=False):
11001100
"""Say something to Mongo.
11011101
11021102
Raises ConnectionFailure if the message cannot be sent. Raises
@@ -1109,11 +1109,9 @@ def _send_message(self, message,
11091109
- `message`: message to send
11101110
- `with_last_error`: check getLastError status after sending the
11111111
message
1112-
- `check_primary`: don't try to write to a non-primary; see
1113-
kill_cursors for an exception to this rule
11141112
"""
11151113
member = self.__ensure_member()
1116-
if check_primary and not with_last_error and not self.is_primary:
1114+
if not with_last_error and not self.is_primary:
11171115
# The write won't succeed, bail as if we'd done a getLastError
11181116
raise AutoReconnect("not master")
11191117

@@ -1345,8 +1343,24 @@ def kill_cursors(self, cursor_ids):
13451343
"""
13461344
if not isinstance(cursor_ids, list):
13471345
raise TypeError("cursor_ids must be a list")
1348-
return self._send_message(
1349-
message.kill_cursors(cursor_ids), check_primary=False)
1346+
1347+
member = self.__member
1348+
1349+
# We're disconnected, but can't risk taking the lock to reconnect
1350+
# if we're being called from Cursor.__del__, see PYTHON-799.
1351+
if not member:
1352+
raise AutoReconnect()
1353+
1354+
_, kill_cursors_msg = message.kill_cursors(cursor_ids)
1355+
sock_info = self.__socket(member)
1356+
try:
1357+
try:
1358+
sock_info.sock.sendall(kill_cursors_msg)
1359+
except:
1360+
sock_info.close()
1361+
raise
1362+
finally:
1363+
member.maybe_return_socket(sock_info)
13501364

13511365
def server_info(self):
13521366
"""Get information about the MongoDB server we're connected to.

pymongo/pool.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,12 @@ def reset(self):
213213
self.pool_id += 1
214214
self.pid = os.getpid()
215215

216-
sockets = None
216+
# Allocate outside the lock. Triggering a GC while holding the lock
217+
# could run Cursor.__del__ and deadlock. See PYTHON-799.
218+
new_sockets = set()
219+
self.lock.acquire()
217220
try:
218-
# Swapping variables is not atomic. We need to ensure no other
219-
# thread is modifying self.sockets, or replacing it, in this
220-
# critical section.
221-
self.lock.acquire()
222-
sockets, self.sockets = self.sockets, set()
221+
sockets, self.sockets = self.sockets, new_sockets
223222
finally:
224223
self.lock.release()
225224

0 commit comments

Comments
 (0)