# Queue termination further design

**URL:** <https://discuss.python.org/t/queue-termination-further-design/26630>\
**Category:** Ideas\
**Created:** [May 10, 2023, 4:25am UTC](https://discuss.python.org/t/queue-termination-further-design/26630 "2023-05-10T04:25:44Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [May 10, 2023, 4:25am UTC](https://discuss.python.org/t/queue-termination-further-design/26630/1 "2023-05-10T04:25:45Z")

</div>

This is a follow-on of [Queue termination](https://discuss.python.org/t/queue-termination/18386) after implementation brought to light some aspects not considered during the design.

@guido brought up the following points regarding design decisions to be made:

> - What should full() and empty() return when the queue is shut down?
> - Do we need a new inquiry method (is\_alive()?) to tell whether a queue has been shut down?
> - When the queue is shut down and empty, should get\_nowait() raise QueueShutDown or QueueEmpty?
> - How do task\_done() and join() interact with shutting down?

In addition, Guido asked about the necessity of `immediate=True` (argument to queue `shutdown`), and you see my comments in the original Discourse topic, but I argue the benefit outweighs the implementation compexity.

Right now the PRs are in a good state with comprehensive testing (although I’m still not convinced [my gist](https://gist.github.com/EpicWink/2eb68def04c1f3aaaeb828e3a5aeaebd) has a bug regarding multiprocessing queue immediate shutdown), so see them for the current decisions made for the design, but in brief:

- `full` and `empty` haven’t had any changes
- There is no `is_alive` (or `is_shutdown`, or `get_shutdown_state`) method
- `get` and `get_nowait` raise when queue is empty and has been shutdown
- `task_done` and `join` raise when the queue is shutdown immediately, and don’t when shutdown eventually

Relavant links:

- [GitHub issue](https://github.com/python/cpython/issues/96471)
- [Threading PR](https://github.com/python/cpython/pull/104225)
- [Asyncio PR](https://github.com/python/cpython/pull/104228)
- [Multiprocessing PR](https://github.com/python/cpython/pull/104230)

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [May 17, 2023, 9:34pm UTC](https://discuss.python.org/t/queue-termination-further-design/26630/2 "2023-05-17T21:34:35Z")

</div>

Sorry for being slow. This is definitely going to target 3.13, so I am focusing on things that need my attention more immediately. But thanks for taking my questions seriously, and we will get through this!

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [May 22, 2023, 2:48pm UTC](https://discuss.python.org/t/queue-termination-further-design/26630/3 "2023-05-22T14:48:58Z")

</div>

An alternate implementation (for `threading` queues for now) where `immediate=True` simply consumes the queue: [gh-96471: Add threading queue shutdown by EpicWink · Pull Request #104750 · python/cpython · GitHub](https://github.com/python/cpython/pull/104750)

---

<div class="post-metadata">

**Author:** ![Tinche](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tinche/32/12507_2.png) [@Tinche](https://discuss.python.org/u/Tinche)\
**Post date:** [May 22, 2023, 3:58pm UTC](https://discuss.python.org/t/queue-termination-further-design/26630/4 "2023-05-22T15:58:51Z")

</div>

Tangentially related, but do we want to make queues (especially asyncio queues) iterable?

A queue being terminated and emptied maps nicely to the `async for` loop exiting. Feels like an easy win.

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [May 22, 2023, 5:39pm UTC](https://discuss.python.org/t/queue-termination-further-design/26630/5 "2023-05-22T17:39:10Z")

</div>

> [@Tinche](#):
>
> Tangentially related, but do we want to make queues (especially asyncio queues) iterable?

So what would `Queue. __anext__ ` look like? Maybe this?

```py
def __anext__ (self):
    try:
        return await self.get()
    except ShutDown:
        raise StopIteration

```

(` __aiter__ ` would just return `self`)

Alternatively, ` __aiter__ ` could be an async generator:

```py
def __aiter__ (self):
    while True:
        try:
            value = await self.get()
        except ShutDown:
            return
        try:
            yield value
        finally:
            self.task_done()

```

(I’m not sure where to put the `task_done()` call in the ` __anext__ ` version.)

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [May 22, 2023, 5:59pm UTC](https://discuss.python.org/t/queue-termination-further-design/26630/6 "2023-05-22T17:59:04Z")

</div>

> [@EpicWink](#):
>
> An alternate implementation (for `threading` queues for now) where `immediate=True` simply consumes the queue: [gh-96471: Add threading queue shutdown by EpicWink · Pull Request #104750 · python/cpython · GitHub](https://github.com/python/cpython/pull/104750)

I like this. Maybe this suggests a different name for `immediate`? E.g. `drain=True` or `flush=True` or `discard=True`. The “immediate” term doesn’t really convey the meaning (perhaps), plus the English half of my brain wants to change it to `immediately=True`.

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [May 22, 2023, 11:00pm UTC](https://discuss.python.org/t/queue-termination-further-design/26630/7 "2023-05-22T23:00:09Z")

</div>

> [@guido](#):
>
> Maybe this suggests a different name for `immediate`? E.g. `drain=True` or `flush=True` or `discard=True`.

I think so. `flush` feels more idiomatic to me, but `discard` better informs the user of the data loss. I’ll rename the parameter in the consuming implementation later today. Any further arguments for other names will likely change my mind.

---

<div class="post-metadata">

**Author:** ![Tinche](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tinche/32/12507_2.png) [@Tinche](https://discuss.python.org/u/Tinche)\
**Post date:** [May 23, 2023, 12:03am UTC](https://discuss.python.org/t/queue-termination-further-design/26630/8 "2023-05-23T00:03:55Z")

</div>

Yeah, the ` __aiter__ ` version looks good at first blush.

Processing items from a queue using this feels much better than the ol’

```python
while True:
    item = await queue.get()
    if item is sentinel:
        break

```

(or I guess the modern `except ShutDown:` now)

… with the unfortunate side-effect of not being able to stick a timeout on the `queue.get()` operation. But I feel like this is a broader problem with async iterators, and ideally would be solved by an independent helper function which is out of scope here in any case, so never mind that now.

What do you think?

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [May 23, 2023, 12:21am UTC](https://discuss.python.org/t/queue-termination-further-design/26630/9 "2023-05-23T00:21:07Z")

</div>

You _could_ execute it inside `async with asyncio.timeout(10) as to:` and then keep calling `to.reschedule(asyncio.get_running_loop().time() + 10)`. But you’re quickly entering the area where the cure is worse than the disease. And why not assume that either a new item will _eventually_ appear in the queue, _or_ the queue will be shut down?

---

<div class="post-metadata">

**Author:** ![Tinche](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tinche/32/12507_2.png) [@Tinche](https://discuss.python.org/u/Tinche)\
**Post date:** [May 23, 2023, 10:35am UTC](https://discuss.python.org/t/queue-termination-further-design/26630/10 "2023-05-23T10:35:51Z")

</div>

You make great points, for some reason I didn’t think of the rescheduling approach 😇

---

<div class="post-metadata">

**Author:** ![Tinche](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tinche/32/12507_2.png) [@Tinche](https://discuss.python.org/u/Tinche)\
**Post date:** [May 28, 2023, 7:06pm UTC](https://discuss.python.org/t/queue-termination-further-design/26630/11 "2023-05-28T19:06:58Z")

</div>

@guido if we like the async iterator idea (and it sounds like we do), I’d be happy to put together a PR for it (including tests and docs). Just ping me when the termination stuff gets merged.

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [April 8, 2024, 5:47am UTC](https://discuss.python.org/t/queue-termination-further-design/26630/12 "2024-04-08T05:47:03Z")

</div>

[asyncio](https://github.com/python/cpython/pull/104228) and [threading](https://github.com/python/cpython/pull/104750) queue shutdown will be in Python 3.13.

* * *

Multiprocessing queue shutdown is still [in development](https://github.com/python/cpython/pull/104230), but I don’t think it’s as important as it’s a lot easier to kill a process externally than a thread. It would be nice to have in to keep the APIs consistent, and I’ll make an attempt with the time I have before the feature-freeze.

* * *

@eric.snow has [commented](https://github.com/python/cpython/issues/96471#issuecomment-2025668020) that sub-interpreter queue shutdown is possible, but I haven’t started work on it as I don’t have any familiarity with sub-interpreters, and the sub-interpreter standard-library module proposal [PEP 734](https://peps.python.org/pep-0734/) isn’t yet accepted.

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [April 18, 2024, 12:47am UTC](https://discuss.python.org/t/queue-termination-further-design/26630/13 "2024-04-18T00:47:53Z")

</div>

Update on multiprocessing queue termination: @YvesDup and I have [notices](https://github.com/python/cpython/pull/104230#issuecomment-2059548365) that the implementation makes it difficult to cleanly implement shutdown without changing the behaviour of the existing implementation. I suggest two options:

- Introduce a new `multiprocessing.queues.TerminableQueue` which simply has the same API as [`queue.Queue`](https://docs.python.org/3/library/queue.html#queue.Queue) (perhaps without `join()` and `task_done()`, as we could add another subclass which uses the existing [`multiprocessing.JoinableQueue`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.JoinableQueue)’s implementation)

- Change how the current `get()` implementation works by injecting a [select](https://docs.python.org/3/library/select.html#select.select) call before `self._reader.recv_bytes` (`self._reader` is a [`multiprocessing.connection.Connection`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.connection.Connection) object):

---

<div class="post-metadata">

**Author:** ![davidism](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/davidism/32/3295_2.png) [@davidism](https://discuss.python.org/u/davidism)\
**Post date:** [August 18, 2026, 1:20pm UTC](https://discuss.python.org/t/queue-termination-further-design/26630/15 "2026-08-18T13:20:20Z")

</div>


