From 4418e5f034cb415819531db17c14f1f065fb4b45 Mon Sep 17 00:00:00 2001 From: Tyler Willey Date: Fri, 24 Jul 2020 10:49:24 -0600 Subject: [PATCH 1/3] If gevent raises a Timeout during self.lock acquisition, a _socket_semaphore count will be lost --- pymongo/pool.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pymongo/pool.py b/pymongo/pool.py index a18e005ba5..46c365f9ab 100644 --- a/pymongo/pool.py +++ b/pymongo/pool.py @@ -1257,8 +1257,17 @@ def _get_socket(self, all_credentials): if not self._socket_semaphore.acquire( True, self.opts.wait_queue_timeout): self._raise_wait_queue_timeout() - with self.lock: - self.active_sockets += 1 + + try: + with self.lock: + self.active_sockets += 1 + except Exception: + self._socket_semaphore.release() + + if self.enabled_for_cmap: + self.opts.event_listeners.publish_connection_check_out_failed( + self.address, ConnectionCheckOutFailedReason.CONN_ERROR) + raise # We've now acquired the semaphore and must release it on error. sock_info = None From 921e6587edf04aac6edca11fc633d46439199cb8 Mon Sep 17 00:00:00 2001 From: Tyler Willey Date: Mon, 27 Jul 2020 08:58:09 -0600 Subject: [PATCH 2/3] Using with will release the condition even on exception being raised --- pymongo/thread_util.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/pymongo/thread_util.py b/pymongo/thread_util.py index 3869ec322f..0cf0a127f2 100644 --- a/pymongo/thread_util.py +++ b/pymongo/thread_util.py @@ -40,22 +40,21 @@ def acquire(self, blocking=True, timeout=None): raise ValueError("can't specify timeout for non-blocking acquire") rc = False endtime = None - self._cond.acquire() - while self._value == 0: - if not blocking: - break - if timeout is not None: - if endtime is None: - endtime = _time() + timeout - else: - timeout = endtime - _time() - if timeout <= 0: - break - self._cond.wait(timeout) - else: - self._value = self._value - 1 - rc = True - self._cond.release() + with self._cond: + while self._value == 0: + if not blocking: + break + if timeout is not None: + if endtime is None: + endtime = _time() + timeout + else: + timeout = endtime - _time() + if timeout <= 0: + break + self._cond.wait(timeout) + else: + self._value = self._value - 1 + rc = True return rc __enter__ = acquire From 918ad32fd21615466a83e9c380a2f544f8abd4a1 Mon Sep 17 00:00:00 2001 From: Tyler Willey Date: Wed, 29 Jul 2020 11:24:49 -0600 Subject: [PATCH 3/3] Change to use flag so that the same exception handling is used --- pymongo/pool.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pymongo/pool.py b/pymongo/pool.py index 46c365f9ab..92814e8570 100644 --- a/pymongo/pool.py +++ b/pymongo/pool.py @@ -1258,20 +1258,14 @@ def _get_socket(self, all_credentials): True, self.opts.wait_queue_timeout): self._raise_wait_queue_timeout() + # We've now acquired the semaphore and must release it on error. + sock_info = None + incremented = False try: with self.lock: self.active_sockets += 1 - except Exception: - self._socket_semaphore.release() + incremented = True - if self.enabled_for_cmap: - self.opts.event_listeners.publish_connection_check_out_failed( - self.address, ConnectionCheckOutFailedReason.CONN_ERROR) - raise - - # We've now acquired the semaphore and must release it on error. - sock_info = None - try: while sock_info is None: try: with self.lock: @@ -1288,8 +1282,10 @@ def _get_socket(self, all_credentials): # We checked out a socket but authentication failed. sock_info.close_socket(ConnectionClosedReason.ERROR) self._socket_semaphore.release() - with self.lock: - self.active_sockets -= 1 + + if incremented: + with self.lock: + self.active_sockets -= 1 if self.enabled_for_cmap: self.opts.event_listeners.publish_connection_check_out_failed(