Skip to content

Commit 4a8cfac

Browse files
[3.14] gh-155997: Fix list_all() if an interpreter is destroyed during the call (GH-155998) (GH-156005)
Creating the Interpreter objects can start a garbage collection which finalizes an object owning the last reference to a listed interpreter. Skip interpreters which no longer exist instead of failing. (cherry picked from commit 60dff5a) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 0a83732 commit 4a8cfac

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

Lib/concurrent/interpreters/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,14 @@ def create():
6868

6969
def list_all():
7070
"""Return all existing interpreters."""
71-
return [Interpreter(id, _whence=whence)
72-
for id, whence in _interpreters.list_all(require_ready=True)]
71+
interps = []
72+
for id, whence in _interpreters.list_all(require_ready=True):
73+
try:
74+
interps.append(Interpreter(id, _whence=whence))
75+
except InterpreterNotFoundError:
76+
# It was destroyed after it was listed.
77+
pass
78+
return interps
7379

7480

7581
def get_current():

Lib/test/test_interpreters/test_api.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,21 @@ def test_idempotent(self):
288288
for interp1, interp2 in zip(actual, expected):
289289
self.assertIs(interp1, interp2)
290290

291+
def test_destroyed_by_gc(self):
292+
# gh-155997: the interpreter is destroyed while list_all() runs.
293+
interp = interpreters.create()
294+
interpid = interp.id
295+
cycle = []
296+
cycle.append(cycle)
297+
cycle.append(interp)
298+
# The cycle holds the only reference, so only the collector frees it.
299+
with support.disable_gc():
300+
del interp, cycle
301+
302+
with support.gc_threshold(1):
303+
ids = [i.id for i in interpreters.list_all()]
304+
self.assertNotIn(interpid, ids)
305+
291306
def test_created_with_capi(self):
292307
mainid, *_ = _interpreters.get_main()
293308
interpid1 = _interpreters.create()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`concurrent.interpreters.list_all`. It failed if an interpreter
2+
was destroyed during the call, in particular by a garbage collection which
3+
finalized the object owning the last reference to it.

0 commit comments

Comments
 (0)