PYTHON-799 Avoid deadlock in Cursor destructor with PyPy.

This commit is contained in:
A. Jesse Jiryu Davis 2014-12-12 18:34:26 -05:00
parent e93c2ac72c
commit 8ebd553d5a
2 changed files with 25 additions and 12 deletions

View File

@ -1096,7 +1096,7 @@ class MongoClient(common.BaseObject):
return message
def _send_message(self, message,
with_last_error=False, command=False, check_primary=True):
with_last_error=False, command=False):
"""Say something to Mongo.
Raises ConnectionFailure if the message cannot be sent. Raises
@ -1109,11 +1109,9 @@ class MongoClient(common.BaseObject):
- `message`: message to send
- `with_last_error`: check getLastError status after sending the
message
- `check_primary`: don't try to write to a non-primary; see
kill_cursors for an exception to this rule
"""
member = self.__ensure_member()
if check_primary and not with_last_error and not self.is_primary:
if not with_last_error and not self.is_primary:
# The write won't succeed, bail as if we'd done a getLastError
raise AutoReconnect("not master")
@ -1345,8 +1343,24 @@ class MongoClient(common.BaseObject):
"""
if not isinstance(cursor_ids, list):
raise TypeError("cursor_ids must be a list")
return self._send_message(
message.kill_cursors(cursor_ids), check_primary=False)
member = self.__member
# We're disconnected, but can't risk taking the lock to reconnect
# if we're being called from Cursor.__del__, see PYTHON-799.
if not member:
raise AutoReconnect()
_, kill_cursors_msg = message.kill_cursors(cursor_ids)
sock_info = self.__socket(member)
try:
try:
sock_info.sock.sendall(kill_cursors_msg)
except:
sock_info.close()
raise
finally:
member.maybe_return_socket(sock_info)
def server_info(self):
"""Get information about the MongoDB server we're connected to.

View File

@ -213,13 +213,12 @@ class Pool:
self.pool_id += 1
self.pid = os.getpid()
sockets = None
# Allocate outside the lock. Triggering a GC while holding the lock
# could run Cursor.__del__ and deadlock. See PYTHON-799.
new_sockets = set()
self.lock.acquire()
try:
# Swapping variables is not atomic. We need to ensure no other
# thread is modifying self.sockets, or replacing it, in this
# critical section.
self.lock.acquire()
sockets, self.sockets = self.sockets, set()
sockets, self.sockets = self.sockets, new_sockets
finally:
self.lock.release()