GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — test_concurrent_gi_frame is flaky under --parallel-threads: the reader thread may never run its loop body · Issue #161489 · python/cpython

test_concurrent_gi_frame is flaky under --parallel-threads: the reader thread may never run its loop body #161489

New issue
Copy link
New issue
Copy link
Open
Open
test_concurrent_gi_frame is flaky under --parallel-threads: the reader thread may never run its loop body#161489
Copy link
Labels
testsTests in the Lib/test dirTests in the Lib/test dirtopic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@thalesmourabh
thalesmourabh
opened on Nov 6, 2026
Issue body actions

Bug report

Bug description:

test_free_threading.test_generators.TestFTGenerators.test_concurrent_gi_frame fails intermittently with AssertionError: 0 != 1 when run under --parallel-threads. The failure is in the test, not in the runtime.

FAIL: test_concurrent_gi_frame (test.test_free_threading.test_generators.TestFTGenerators.test_concurrent_gi_frame)
AssertionError: 0 != 1

Reproducer

On a --with-pydebug --disable-gil build at 534605904dfa7db25bf19a1b959411b8562c2b80:

./python -m test test_free_threading.test_generators -F --parallel-threads=4 -j10 -W

5 out of 10 runs reproduced, after 126 s, 160 s, 320 s, 368 s and 391 s. Build: GCC 15.2.0, 10 cores. Not sensitive to mimalloc purge settings (2/5 with MIMALLOC_PURGE_DELAY=0 MIMALLOC_ARENA_PURGE_MULT=1, 3/5 without).

Cause

0 != 1 can only mean len(frames) == 0. I instrumented the test locally to record, on failure, how many times the reader loop body executed and how often gi_frame was non-None:

frames=0 iterations=0 non_none=0

iterations=0 — the reader loop body never executed. The runner consumed all 10 000 yields and called done.set() before reader reached its first while not done.is_set() check, so frames stayed empty:

def reader():
    while not done.is_set():        # already True on first evaluation
        frame = g.gi_frame
        ...
        frames.add(frame)
    self.assertIsNone(g.gi_frame)

threading_helper.run_concurrently([runner, reader])
self.assertEqual(len(frames), 1)    # 0 != 1

This rules out the alternative reading, which would have been a real bug: gi_frame returning None for a live generator. That would show a large iteration count with non_none=0; it does not happen.

Why --parallel-threads triggers it

Lib/test/libregrtest/parallel_case.py makes num_threads copies of the test case and releases them from one threading.Barrier simultaneously, in the same process. Each copy starts its own runner/reader pair, so --parallel-threads=4 -j10 is roughly 80 threads on 10 cores. Under that oversubscription a reader can fail to be scheduled before its runner finishes — and 10 000 trivial yields finish quickly.

Independent confirmation outside regrtest

Both kinds of oversubscription are needed; neither alone is sufficient. Replicating the test body standalone on the same build:

Condition Method executions frames empty min. loop iterations
1 copy, 1 process 300 0 271
1 copy, 24 processes 1440 0 196
4 copies, 1 process 800 0 67
4 copies × 10 processes 1600 3 0

All three captured cases were (len=0, iterations=0, non_none=0), matching the instrumented run. Note the last column decreasing monotonically with load until it reaches zero: the same phenomenon at increasing scale.

Note

--parallel-threads=4 is the configuration used in .github/workflows/reusable-san.yml (--tsan-parallel --parallel-threads=4), so this is a plausible source of intermittent CI failures.

The assertion the test appears to care about — that the reader never observes two distinct frame objects, per gh-144446 — held in every run I saw (frames > 1 never occurred in the 2400 executions where I tracked it). len(frames) == 1 additionally requires the reader to be scheduled, which is not guaranteed. I have no opinion on which way to resolve that, since tolerating zero and synchronising to guarantee one iteration cover different things.

Test added in #156037 for gh-144446; present since v3.16.0a1.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

  • gh-161489: Make concurrent gi_frame test scheduling-independent #161585

Activity

  1. thalesmourabh
    added a commit that references this issue on Nov 6, 2026

    Abrir a gh-161489 no CPython e registrar

    ea98e7b
  2. NekoAsakura
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    testsTests in the Lib/test dirTests in the Lib/test dir
    topic-free-threading
    on Nov 6, 2026
  3. localzet
    added a commit that references this issue on Nov 7, 2026

    pythongh-161489: Ensure reader inspects a live generator in concurren…

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    testsTests in the Lib/test dirTests in the Lib/test dirtopic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.