]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-150042: queue.SimpleQueue.put: fix minor refleak. (GH-150043)
authorlarryhastings <larry@hastings.org>
Tue, 19 May 2026 22:27:04 +0000 (15:27 -0700)
committerGitHub <noreply@github.com>
Tue, 19 May 2026 22:27:04 +0000 (00:27 +0200)
If queue.SimpleQueue.put can't handoff the item to a
waiting thread, and fails to allocate memory when adding
the item to a ringbuf, it would leak a reference.  Fixed.

Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-16-54-54.gh-issue-150042.LSr5W8.rst [new file with mode: 0644]
Modules/_queuemodule.c

diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-16-54-54.gh-issue-150042.LSr5W8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-16-54-54.gh-issue-150042.LSr5W8.rst
new file mode 100644 (file)
index 0000000..18a4fbd
--- /dev/null
@@ -0,0 +1 @@
+Fix refleak in queue.SimpleQueue.put if memory allocation fails.
index ed925f3525a9a7db62678980ac58a977369b98c2..d5ba36273c82626bca9c1d1737d0272be885ce83 100644 (file)
@@ -154,8 +154,6 @@ RingBuf_Get(RingBuf *buf)
 }
 
 // Returns 0 on success or -1 if the buffer failed to grow.
-//
-// Steals a reference to item.
 static int
 RingBuf_Put(RingBuf *buf, PyObject *item)
 {
@@ -165,11 +163,10 @@ RingBuf_Put(RingBuf *buf, PyObject *item)
         // Buffer is full, grow it.
         if (resize_ringbuf(buf, buf->items_cap * 2) < 0) {
             PyErr_NoMemory();
-            Py_DECREF(item);
             return -1;
         }
     }
-    buf->items[buf->put_idx] = item;
+    buf->items[buf->put_idx] = Py_NewRef(item);
     buf->put_idx = (buf->put_idx + 1) % buf->items_cap;
     buf->num_items++;
     return 0;
@@ -276,16 +273,13 @@ maybe_handoff_item(void *arg, void *park_arg, int has_more_waiters)
 {
     HandoffData *data = (HandoffData*)arg;
     PyObject **item = (PyObject**)park_arg;
-    if (item == NULL) {
-        // No threads were waiting
-        data->handed_off = false;
-    }
-    else {
+    data->queue->has_threads_waiting = has_more_waiters;
+
+    data->handed_off = item != NULL;
+    if (data->handed_off) {
         // There was at least one waiting thread, hand off the item
-        *item = data->item;
-        data->handed_off = true;
+        *item = Py_NewRef(data->item);
     }
-    data->queue->has_threads_waiting = has_more_waiters;
 }
 
 /*[clinic input]
@@ -307,21 +301,22 @@ _queue_SimpleQueue_put_impl(simplequeueobject *self, PyObject *item,
                             int block, PyObject *timeout)
 /*[clinic end generated code: output=4333136e88f90d8b input=a16dbb33363c0fa8]*/
 {
-    HandoffData data = {
-        .handed_off = 0,
-        .item = Py_NewRef(item),
-        .queue = self,
-    };
     if (self->has_threads_waiting) {
+        HandoffData data = {
+            .handed_off = 0,
+            .item = item,
+            .queue = self,
+        };
         // Try to hand the item off directly if there are threads waiting
         _PyParkingLot_Unpark(&self->has_threads_waiting,
                              maybe_handoff_item, &data);
-    }
-    if (!data.handed_off) {
-        if (RingBuf_Put(&self->buf, item) < 0) {
-            return NULL;
+        if (data.handed_off) {
+            Py_RETURN_NONE;
         }
     }
+    if (RingBuf_Put(&self->buf, item) < 0) {
+        return NULL;
+    }
     Py_RETURN_NONE;
 }