]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
backport mhammond's patch:
authorAnthony Baxter <anthonybaxter@gmail.com>
Fri, 19 Apr 2002 03:54:37 +0000 (03:54 +0000)
committerAnthony Baxter <anthonybaxter@gmail.com>
Fri, 19 Apr 2002 03:54:37 +0000 (03:54 +0000)
Fix bug 544473 - "Queue module can deadlock".
Use try/finally to ensure all Queue locks remain stable.
Includes test case.  Bugfix candidate.

Original patch(es):
python/dist/src/Lib/Queue.py:1.15

Lib/Queue.py

index 0e6bbf055a469a4e2013c60219e36bd7ac5e5d2b..de7be72d7a9d985db61f77cb4d6c91c5711fbdff 100644 (file)
@@ -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):