# Understanding "list(set) should be atomic in the free-threaded build"

**URL:** <https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305>\
**Category:** Python Help\
**Tags:** free-threading\
**Created:** [March 12, 2024, 1:59pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305 "2024-03-12T13:59:08Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [March 12, 2024, 1:59pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/1 "2024-03-12T13:59:08Z")

</div>

I just saw [this issue](https://github.com/python/cpython/issues/116621) and am wondering.

The code

```python
def _cleanup(): 
     # check for processes which have finished 
     for p in list(_children): 
         if (child_popen := p._popen) and child_popen.poll() is not None: 
             _children.discard(p)

```

is said to construct a list from a set, while the set may be concurrently updated, and that in the free-threaded build, this may lead to `RuntimeError: Set changed size during iteration`.

How so? Is `list(_children)` becoming lazy, somehow iterator-like? So that it isn’t fully built anymore before the `for` statement starts the iteration?

@colesbury

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [March 12, 2024, 2:10pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/2 "2024-03-12T14:10:40Z")

</div>

> [@Stefan2](#):
>
> Is `list(_children)` becoming lazy, somehow iterator-like?

The call to `list` itself must iterate over `_children` to create the list. If I understand the issue correctly, the problem is that `_children` can be modified by another thread during that operation, leading to an error.

That is to say, ignore the body of the `for` statement entirely. The error happens during `list(_children)`

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [March 12, 2024, 2:12pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/3 "2024-03-12T14:12:27Z")

</div>

And that other thread is already executing the loop body before the list is fully built?

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [March 12, 2024, 2:16pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/4 "2024-03-12T14:16:22Z")

</div>

Wait, you mean the “concurrently updated” doesn’t refer to the updates shown in that code (those `_children.discard(p)` calls)? The issue is about **any** `list(some set)`? Then I find that example misleading…

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [March 12, 2024, 2:16pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/5 "2024-03-12T14:16:29Z")

</div>

The other thread could be doing something totally different, this issue is unrelated to the `for` loop. I think Sam was just providing an example from the stdlib to show that this needs to be fixed, but it ended up being confusing.

You have two threads that use a set, call it `_children`. In one thread you create a list from it with `list(_children)`. If another thread adds or removes elements from `_children` _while you are creating the list_, you get a `RuntimeError` because `list(_children)` is iterating over the set and it changed size. That’s why Sam raised the issue to make `list(some_type)` atomic \[1\].

(all of this is just my best understanding of the issue, I might be wrong! But I think this is it)

* * *

1. the problem can affect other builtin objects too

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [March 12, 2024, 2:22pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/6 "2024-03-12T14:22:46Z")

</div>

Yeah ok, if it’s not about the updates in that loop, then it’s clear. Creating a list from a set seems like an ordinary thing to do (ok, probably less common than the other way around), so the way the issue is written, specifically calling out that “pattern” in the code, made me think it’s about those updates shown in that code.

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [March 12, 2024, 2:39pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/7 "2024-03-12T14:39:40Z")

</div>

Tha claim in the issue seems wrong to me.

It assumes that the list(set) will retain a reference to the set and if the set changes then the iteration will fail.

But that is not how it works. The list is fully formed from the set and then the set is no longer referenced. You are right that there is an assumption that list(set) is lazy.

I think you can reply with that analysis and close the issue.

---

<div class="post-metadata">

**Author:** ![kknechtel](https://avatars.discourse-cdn.com/v4/letter/k/e47c2d/32.png) [@kknechtel](https://discuss.python.org/u/kknechtel)\
**Post date:** [March 12, 2024, 3:04pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/8 "2024-03-12T15:04:18Z")

</div>

> [@barry-scott](#):
>
> But that is not how it works. The list is fully formed from the set and then the set is no longer referenced.

This doesn’t matter.

Introducing free-threading means that another thread could modify the set _during the process of the list “being fully formed” from the set_. At the Python level, creating a list from a set is a single, currently-non-interruptible operation. But at the machine level, it is very many operations, and by default there is nothing to prevent another thread from using the set in any way in between.

Avoiding that sort of problem (and many others) is what the GIL got you - at the cost of making threading essentially useless for actually boosting performance (by taking advantage of additional processors).

The claim has nothing to do with the `for` loop in the Python code. That’s the point of the rest of the discussion in the thread.

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [March 12, 2024, 4:49pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/9 "2024-03-12T16:49:34Z")

</div>

> [@kknechtel](#):
>
> Introducing free-threading means that another thread could modify the set _during the process of the list “being fully formed” from the set_

Totally agree with you, and I assume a lock must be used to prevent this happening.

Isn’t this a case of assuming the core devs will not do a sane jobs for free threading? Surely this is something to verify once there is a version of free threading to code review and test?

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [March 12, 2024, 4:59pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/10 "2024-03-12T16:59:14Z")

</div>

> [@barry-scott](#):
>
> Isn’t this a case of assuming the core devs will not do a sane jobs for free threading? Surely this is something to verify once there is a version of free threading to code review and test?

This topic is literally about core devs fixing the issue in CPython so that this works correctly.

---

<div class="post-metadata">

**Author:** ![colesbury](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/colesbury/32/28031_2.png) [@colesbury](https://discuss.python.org/u/colesbury)\
**Post date:** [March 12, 2024, 4:59pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/11 "2024-03-12T16:59:30Z")

</div>

What @jamestwebber and @kknechtel wrote sounds correct to me!

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [March 12, 2024, 11:02pm UTC](https://discuss.python.org/t/understanding-list-set-should-be-atomic-in-the-free-threaded-build/48305/12 "2024-03-12T23:02:06Z")

</div>

> [@jamestwebber](#):
>
> This topic is literally about core devs fixing the issue in CPython so that this works correctly.

Okay… I was not aware that there was enough code in place to know this would be broken yet.  
I’m clearly not keeping up. I’ll step back.
