diff options
author | Mark Hammond <mhammond@skippinet.com.au> | 2002-04-19 00:11:32 (GMT) |
---|---|---|
committer | Mark Hammond <mhammond@skippinet.com.au> | 2002-04-19 00:11:32 (GMT) |
commit | 3b959dbcaffe7fce27b21cde42b7d77a8ac29191 (patch) | |
tree | 09de8af2a8e1c6eacae679bb75da0dcf5ecbcb10 /Lib/Queue.py | |
parent | 08d821582faab8f3b6eeab1b1fd7520417b82b80 (diff) | |
download | cpython-3b959dbcaffe7fce27b21cde42b7d77a8ac29191.zip cpython-3b959dbcaffe7fce27b21cde42b7d77a8ac29191.tar.gz cpython-3b959dbcaffe7fce27b21cde42b7d77a8ac29191.tar.bz2 |
Fix bug 544473 - "Queue module can deadlock".
Use try/finally to ensure all Queue locks remain stable.
Includes test case. Bugfix candidate.
Diffstat (limited to 'Lib/Queue.py')
-rw-r--r-- | Lib/Queue.py | 47 |
1 files changed, 33 insertions, 14 deletions
diff --git a/Lib/Queue.py b/Lib/Queue.py index 0e6bbf0..de7be72 100644 --- a/Lib/Queue.py +++ b/Lib/Queue.py @@ -55,13 +55,24 @@ class Queue: elif not self.fsema.acquire(0): raise Full self.mutex.acquire() - was_empty = self._empty() - self._put(item) - if was_empty: - self.esema.release() - if not self._full(): - self.fsema.release() - self.mutex.release() + release_fsema = True + try: + was_empty = self._empty() + self._put(item) + # If we fail before here, the empty state has + # not changed, so we can skip the release of esema + if was_empty: + self.esema.release() + # If we fail before here, the queue can not be full, so + # release_full_sema remains True + release_fsema = not self._full() + finally: + # Catching system level exceptions here (RecursionDepth, + # OutOfMemory, etc) - so do as little as possible in terms + # of Python calls. + if release_fsema: + self.fsema.release() + self.mutex.release() def put_nowait(self, item): """Put an item into the queue without blocking. @@ -84,13 +95,21 @@ class Queue: elif not self.esema.acquire(0): raise Empty self.mutex.acquire() - was_full = self._full() - item = self._get() - if was_full: - self.fsema.release() - if not self._empty(): - self.esema.release() - self.mutex.release() + release_esema = True + try: + was_full = self._full() + item = self._get() + # If we fail before here, the full state has + # not changed, so we can skip the release of fsema + if was_full: + self.fsema.release() + # Failure means empty state also unchanged - release_esema + # remains True. + release_esema = not self._empty() + finally: + if release_esema: + self.esema.release() + self.mutex.release() return item def get_nowait(self): |