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 — Crash when calling `textiowrapper_iternext` and writing to a text file simultaneously in ft build · Issue #134908 · python/cpython

Crash when calling textiowrapper_iternext and writing to a text file simultaneously in ft build #134908

New issue
Copy link
New issue
Copy link
Closed
Closed
Crash when calling textiowrapper_iternext and writing to a text file simultaneously in ft build#134908
Copy link
Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump

Description

@duaneg
duaneg
opened on May 30, 2025
Issue body actions

Running this script under free-threading build:

import os
import tempfile
import threading

N=2
COUNT=100

def writer(file, barrier):
    barrier.wait()
    for _ in range(COUNT):
        f.write("x")

def reader(file, stopping):
    while not stopping.is_set():
        for line in file:
            assert line == ""

stopping = threading.Event()
with tempfile.NamedTemporaryFile("w+") as f:
    reader = threading.Thread(target=reader, args=(f, stopping))
    reader.start()

    barrier = threading.Barrier(N)
    writers = [threading.Thread(target=writer, args=(f, barrier))
               for _ in range(N)]
    for t in writers:
        t.start()
    for t in writers:
        t.join()
    stopping.set()
    reader.join()

    f.flush()
    assert(os.stat(f.name).st_size == COUNT * N)

...results in a crash:

python: ./Modules/_io/textio.c:1751: _io_TextIOWrapper_write_impl: Assertion `self->pending_bytes_count == 0' failed.
Aborted (core dumped)

The textiowrapper_iternext method is not protected by a critical section and calls _textiowrapper_readline, which calls _textiowrapper_writeflush, which relies on the GIL or critical section to synchronise access to its internal data. In a free-threading build this means iterating over lines in a text file is not thread-safe and can crash if it races with writes or other operations.

It looks like all other entry points that could call _textiowrapper_writeflush are protected by the critical section, so the easiest fix is probably to just likewise protect textiowrapper_iternext.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

No response

Linked PRs

  • gh-134908: protect textiowrapper_iternext with critical section #134910
  • [3.14] gh-134908: Protect textiowrapper_iternext with critical section (gh-134910) #135039
  • [3.13] gh-134908: Protect textiowrapper_iternext with critical section (gh-134910) #135040

Activity

  1. duaneg
    added
    type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
    on May 30, 2025
  2. duaneg
    added a commit that references this issue on May 30, 2025

    pythongh-134908: protect ``textiowrapper_iternext`` with critical sec…

    588a5b6
  3. duaneg commented on May 30, 2025

    @duaneg
    duaneg
    on May 30, 2025
    ContributorAuthor
    More actions

    Note that I found this while investigating #118138, however I think it is a separate bug that should be addressed under a separate issue.

    CC @cmaloney who seems to be looking at related issues in this area already 👋

    Reacted by Cody Maloney
  4. ZeroIntensity
    added
    topic-free-threading
    on May 30, 2025
  5. ZeroIntensity commented on May 30, 2025

    @ZeroIntensity
    ZeroIntensity
    on May 30, 2025
    Member
    More actions

    I'm pretty sure we don't support concurrent calls to __next__. You need to manually serialize the calls yourself. See gh-124397.

  6. duaneg commented on May 30, 2025

    @duaneg
    duaneg
    on May 30, 2025
    ContributorAuthor
    More actions

    See gh-124397.

    That is a very interesting discussion, thanks for linking!

    I'm pretty sure we don't support concurrent calls to __next__. You need to manually serialize the calls yourself.

    I totally agree that in general iterators should not be assumed to be thread-safe, in the broader sense. However they should not crash, which is happening here. In the description I reported an assertion failure, but the repro script will crash with assertions disabled too, just in more "interesting" ways.

  7. ZeroIntensity commented on May 30, 2025

    @ZeroIntensity
    ZeroIntensity
    on May 30, 2025
    Member
    More actions

    You can get similar crashes from pure-Python by concurrently calling an object's __init__. I don't think there's much we can do without substantially hurting performance.

  8. duaneg commented on May 30, 2025

    @duaneg
    duaneg
    on May 30, 2025
    ContributorAuthor
    More actions

    I'm sure there are places where fixing crashes without substantially hurting performance might be difficult or impossible, but I don't think this is one of them. It should have no impact on GIL performance and minimal in free-threading.

    The third bullet point in #124397 is:

    Other iterators implemented in C will get only the minimal changes necessary to cause them to not crash in a free-threaded build. The edits should be made in a way that does not impact existing semantics or performance (i.e. do not damage the standard GIL build).

    I think this fix is exactly that...or at least, that was exactly what I intended when I wrote it. Of course, I may well have missed something!

  9. picnixz
    added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    on May 30, 2025
  10. ZeroIntensity commented on May 30, 2025

    @ZeroIntensity
    ZeroIntensity
    on May 30, 2025
    Member
    More actions

    I'll let others weigh in here, but my personal stance is that concurrent iteration without synchronization by the caller is not a real use-case. The example you sent would suffer from so much lock contention that it would be no better than calling it in a single thread :(

    I think that last point is in regards to making the iterator thread-safe when others are calling separate methods on it, not necessarily concurrent calls to __next__. I think this needs clarification, though.

  11. colesbury commented on May 30, 2025

    @colesbury
    colesbury
    on May 30, 2025
    Contributor
    More actions

    From looking at the repro code, this looks like something we should fix.

  12. ZeroIntensity commented on May 30, 2025

    @ZeroIntensity
    ZeroIntensity
    on May 30, 2025
    Member
    More actions

    Ok, thanks Sam. For clarity: when should we fix these cases?

  13. colesbury commented on May 30, 2025

    @colesbury
    colesbury
    on May 30, 2025
    Contributor
    More actions

    This looks like concurrent operations on a NamedTemporaryFile. I think I/O should generally be internally synchronized. That's mostly true for Python already, it's usually true at the OS level, as well as in other language runtimes.

    I get that there is a crash in iteration, but it doesn't look like there's an iterator shared between threads.

  14. ZeroIntensity commented on May 30, 2025

    @ZeroIntensity
    ZeroIntensity
    on May 30, 2025
    Member
    More actions

    The NamedTemporaryFile is the iterator, and that's shared between threads.

    Reacted by Sam Gross
  15. colesbury commented on May 30, 2025

    @colesbury
    colesbury
    on May 30, 2025
    Contributor
    More actions

    Thanks. That still feels pretty different from list and dict iterators. For one thing, it'd be difficult to make list and dict iterators thread-safe without slowing down the common case substantially. I don't think that's the case here. The iteration interface just provides access to readline, which is already synchronized.

    I think there's a difference between objects like NamedTemporaryFile that provide an iteration interface and objects like list_iterator, which are usually temporary objects that you don't think about and rarely store to variables.

  16. ZeroIntensity commented on May 30, 2025

    @ZeroIntensity
    ZeroIntensity
    on May 30, 2025
    Member
    More actions

    Ok, that makes sense to me.

  17. colesbury
    added a commit that references this issue on Jun 2, 2025

    gh-134908: Protect `textiowrapper_iternext` with critical section (gh…

    44fb7c3
  18. miss-islington
    added a commit that references this issue on Jun 2, 2025

    pythongh-134908: Protect `textiowrapper_iternext` with critical secti…

    37be220
  19. colesbury
    added 4 commits that reference this issue on Jun 2, 2025

    [3.13] pythongh-134908: Protect `textiowrapper_iternext` with critica…

    731df24

    [3.14] gh-134908: Protect `textiowrapper_iternext` with critical sect…

    428b0ca

    [3.13] pythongh-134908: Protect `textiowrapper_iternext` with critica…

    1f2998b

    [3.13] gh-134908: Protect `textiowrapper_iternext` with critical sect…

    a9b8ffd
  20. colesbury
    closed this as completedon Jun 2, 2025
  21. Pranjal095
    added a commit that references this issue on Jul 12, 2025

    pythongh-134908: Protect `textiowrapper_iternext` with critical secti…

    53856bf
  22. taegyunkim
    added a commit that references this issue on Aug 4, 2025

    pythongh-134908: Protect `textiowrapper_iternext` with critical secti…

    17d41ce
  23. Agent-Hellboy
    added a commit that references this issue on Aug 19, 2025

    pythongh-134908: Protect `textiowrapper_iternext` with critical secti…

    30ed2bd
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

    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402520 — 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.