Process can hang at exit: the non-daemon logistro stderr reader waits on a pipe that chrome_crashpad_handler (orphaned, macOS) still holds

Offen
#319 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Anfängerfreundlichkeit
46/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Tech-Stack
python
Bereich
backend

Rechercherichtung

Start by tracing how choreographer starts and closes the browser process and how logistro.getPipeLogger creates and manages its stderr reader; the issue identifies the reader thread as the point that keeps Python alive. Check whether logistro or choreographer owns that thread’s lifecycle, then reproduce the exit hang on macOS with the reported Chrome and package versions. Done means the reader cannot block interpreter exit while the browser’s stderr pipe remains open, with appropriate coverage for the chosen fix.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

Summary

After fig.to_image() (Kaleido 1.4.0, choreographer 1.4.0, logistro 2.0.1) has returned, the Python process can hang at exit. It stays at 0 % CPU in threading._shutdown, joining the browser_procThread reader that logistro.getPipeLogger("browser_proc") starts. That thread is not a daemon thread and is blocked in os.read on the browser's stderr pipe. It only returns at EOF, and EOF never comes while any process still holds the write end. On macOS, chrome_crashpad_handler holds it.

Environment

  • macOS
  • Python 3.13
  • kaleido 1.4.0, choreographer 1.4.0, logistro 2.0.1, plotly 7.1.0
  • System Chrome 156.0.8078.74 and 156.0.8078.80

What we measured

  • Who holds the pipe. When Chrome starts, a new chrome_crashpad_handler appears with PPID 1 and a session and process group of its own, so it double-forked with setsid. lsof shows its fd 2 as the other end of the logistro reader's pipe, in 5 of 5 runs. Its --database is the user's real Chrome profile (~/Library/Application Support/Google/Chrome/Crashpad), not the temporary --user-data-dir.
  • The handler outlives close(). Right after Browser.close() returns, the handler is still running; it usually exits about 1 to 1.5 s later. While it lives, the reader thread cannot end, and an interpreter that is exiting waits for it.
  • The cause, confirmed. We sent SIGSTOP to the handler after fig.to_image() returned. The process then hangs at exit, every time, and exits at once after SIGCONT.
  • Under real load. In a test suite with 12 concurrent processes, 2 of them passed their tests and then never exited. In the hung processes, sample shows the main thread in threading._shutdown joining two logistro pipe-reader threads blocked in os.read. 14 handlers had been orphaned at the time.
  • Other remedies don't reach it.
    • Killing the browser's process group doesn't reach the handler, because it has left that group and session.
    • --disable-breakpad, which choreographer already passes, doesn't stop macOS Chrome from starting the handler. We didn't test --disable-crash-reporter.
    • Linux and Windows were not measured.

Request

Make the reader thread a daemon thread: Thread(..., daemon=True) in logistro.getPipeLogger, or a way for choreographer to ask for one. A logging reader should not decide whether the interpreter can exit, and no process-group or tree walk can reach a process that has left the group.

A bounded join in Browser.close() would not help on its own. The thread stays non-daemon, so threading._shutdown still joins it at exit. It only helps together with a daemon thread, or with a reader that stops by itself, for example by polling the browser process between reads with a select timeout.

Which project should own the fix is your call, since the thread lives in logistro and choreographer is the consumer. Happy to provide more detail.

Vorherrschende Sprache
Python
Sterne
17
Forks
10
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Entwicklungsumgebung

Dieses Projekt bietet weder Dev-Container noch Dockerfile noch Beitragsleitfaden – die Einrichtung liegt bei Ihnen. Beginnen Sie mit der README; die allgemeinen Schritte stehen in unserem Leitfaden für den ersten Beitrag.

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus plotly/choreographer

Alle Issues in plotly/choreographer

Ähnliche Issues

Weitere Issues zu Python

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.