← 输入框|github.com|刷新
Skip to content

gh-83055: Reserve freed asyncio.Queue slots for woken putters - #160967

Open
asvetlov wants to merge 1 commit into
python:mainfrom
asvetlov:claude/cpython-queue-fairness-6523a2
Open

asvetlov wants to merge 1 commit into
python:mainfrom
asvetlov:claude/cpython-queue-fairness-6523a2

Conversation

@asvetlov

Copy link
Copy Markdown
Contributor

When the queue is full, get_nowait() pops a waiting putter and wakes it, but the freed slot stays open until that putter resumes. A later put() (fast path) or put_nowait() can take it; the woken putter then finds the queue full and is re-appended to the end of _putters, so items are put out of order and a waiter can be starved.

This PR reserves the freed slot for the woken putter:

  • get_nowait() increments _reserved when it wakes a putter; full() counts reserved slots (qsize() + _reserved >= maxsize).
  • The woken putter releases its reservation right before put_nowait(); if it is cancelled after being woken it passes the reservation to the next putter.
  • shutdown() clears the counter.

This is the same idea asyncio.Semaphore uses for fairness (taking the value on the waiter's behalf). Compared to the earlier attempt (gh-112501):

  • no new exceptions; full() is False still guarantees put_nowait() succeeds;
  • O(1), no scanning of done futures;
  • unreserved free slots remain usable, so Serhiy's example (two get_nowait() with one waiting putter, then put_nowait()) succeeds.

Behaviour change: full() may be True while qsize() < maxsize, for as long as a woken putter has not resumed. Documented in full()/put().

Getters are left as is: the issue is about the order items enter the queue, and reserving items would change the meaning of empty().

🤖 Generated with Claude Code

When get_nowait() wakes a waiting putter, the freed slot was open until
the putter resumed, so a later put() or put_nowait() could take it.  The
woken putter then found the queue full again and went to the back of the
line, putting items out of order.

Count slots promised to woken putters in full(), so the slot stays with
the putter that was woken for it.  This keeps put_nowait() consistent with
full() and stays O(1), unlike the approach in pythongh-112501.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@read-the-docs-community

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #35522303 | 📁 Comparing 268b387 against main (6632294)

  🔍 Preview build  

2 files changed
± library/asyncio-queue.html
± whatsnew/changelog.html

@asvetlov
asvetlov marked this pull request as ready for review October 30, 2026 13:30
@asvetlov
asvetlov requested a review from willingc as a code owner October 30, 2026 13:31
@asvetlov

Copy link
Copy Markdown
Contributor Author

@YvesDup here is my try to make asyncio.Queue fair.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant