Skip to content

gfx_display: stop display threads without waiting on the main thread - #376

Merged
techomancer merged 2 commits into
techomancer:mainfrom
jamesbraid:display-stop-deadlock
Oct 31, 2026
Merged

techomancer merged 2 commits into
techomancer:mainfrom
jamesbraid:display-stop-deadlock

Conversation

@jamesbraid

@jamesbraid jamesbraid commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

On macOS, closing the window hung iris for good. main stops the machine, which joins each board's display thread. That thread ended with renderer.stop(), and glutin's make_not_current dispatches synchronously to the main thread, which is blocked in the join. Nothing past that ran, including saving the disks, so overlay writes were lost too.

Every board had it: REX3 (both heads), GR2, IMPACT on the Indigo2 and Octane, and the O2's display through its Drop.

  • gfx_display::DisplayStop is a small helper each board holds. A stop records whether it came from the thread named "main" (std has no is-main-thread test; Rust names the main thread, and a test on Linux can stand in for it), and if so the display thread leaves the renderer alone instead of tearing it down. A stop from any other thread (a reset, a snapshot load) still releases the context on its own thread. GfxDisplay::display_stop() points a new board at the helper; nothing forces it to call it. A main-thread stop is final; the doc says so.
  • The machines now stop their disk controllers first and the graphics board last. The disk flush after the stop is skipped whenever the stop doesn't return, whatever the order. What the reorder keeps is the work the stops do themselves: HPC3 saves the RTC NVRAM and the EEPROM, IOC3 writes its NVRAM, and in-flight disk commands complete.

One test builds one machine per display family (Newport on an Indy, GR2 on an Indigo2, IMPACT on an Octane, the O2's GBE) and gives each a renderer whose stop needs the main thread. Each machine is stopped from a thread named "reset" (the renderer must still be released), started again, and stopped from a thread named "main": that stop must return. The O2 gets a machine for each stop. Without the fix all four main-thread stops hang. Order tests check the Octane stops its SCSI controllers and IOC3, and the Indy its HPC3, before the graphics board, after checking they were running. The tests each use a directory of their own and keep the REX3 shape corpus out of $HOME. Tested on Linux with a renderer that behaves like macOS; not run on a Mac.

…m main

Closing the window on macOS hung iris. main stops the machine, which
joins each graphics board's display thread, and that thread ends by
releasing its renderer's GL context. glutin runs that release on the main
thread, which is the thread waiting in the join, so neither can go on.
iris never exited and the disk overlay's sector list was never saved.

Every board ended its display thread with its own call to the renderer's
stop: REX3 (Indy, Indigo2), GR2, the IMPACT board (Indigo2 IP28 and the
Octane) and the O2's GBE. DisplayStop is now the one place that decides
it. A stop asked for by a thread named "main" means the process is ending
and the window is gone, so the display thread leaves the renderer alone;
that stop is final. A stop from any other thread (a reset) still releases
it on the thread that owns the context, so the next start sets it up
afresh. GfxDisplay has an accessor for the helper, so a new board is
pointed at it; nothing makes a board call it. The O2 requests it from
its drop, since its stop leaves the display thread running. GR2 now
records the request before it clears its running flag, so the display
thread cannot end between the two.

The test builds one machine per display family (Newport on an Indy, GR2
on an Indigo2, IMPACT on an Octane, the O2's GBE) and puts a renderer in
its display whose stop waits for the main thread. A machine is stopped
from a thread named reset, where the renderer must still be released,
started again, and stopped from a thread named main, which never answers:
that stop must return. The O2's display thread ends only when the machine
is dropped, so it gets a machine for each. Each test in this file works in
a directory of its own, and the REX3 shape corpus it saves goes to a temp
file rather than the home directory. Newport shows nothing until a video
mode is programmed, so its configuration selects one rather than wait for
a guest to do it. Without the change all four main-thread stops hang.
Stopping a machine joined the graphics board first (the Octane stopped
its devices in reverse start order, and the board starts last; the Indy
and Indigo2 stopped REX3, the IMPACT board and GR2 before HPC3). A
graphics stop waits on a display thread, and that wait is the one that
can fail to end. When it did, everything stopped after it never ran.

That is not the disk flush. The disk controllers' stops only join their
workers, and the flush that follows the stop in the main thread's
stop-and-flush is skipped whenever the stop does not return, whatever
the order. What the reorder keeps is the work the stops themselves do:
HPC3's stop saves the RTC NVRAM and the EEPROM, IOC3 writes its NVRAM
out, and disk commands still in flight complete.

The board now stops last, on the Octane (stop and drop) and on the Indy
and Indigo2. The tests tear down a started machine's display with a
renderer that notes whether a disk controller is still running at that
moment: HPC3 on the Indy, the ISP1040s and IOC3 on the Octane. Each
first checks that those controllers are running before the stop, so a
machine that stopped starting them would fail rather than pass without
testing anything. Both fail with the old order. The Octane's Ethernet
controller is not checked; it has no public running state. The Indy test
keeps its REX3 shape corpus out of the home directory.
@jamesbraid
jamesbraid marked this pull request as draft October 31, 2026 21:15
@jamesbraid
jamesbraid marked this pull request as ready for review October 31, 2026 21:25
@jamesbraid
jamesbraid marked this pull request as draft October 31, 2026 21:27
@jamesbraid
jamesbraid force-pushed the display-stop-deadlock branch 2 times, most recently from 8696cec to c528316 Compare October 31, 2026 22:30
@jamesbraid
jamesbraid marked this pull request as ready for review October 31, 2026 22:33
@Samueltgg12

Copy link
Copy Markdown
Contributor

@techomancer cool.

@techomancer
techomancer merged commit 4915ca8 into techomancer:main Oct 31, 2026
7 checks passed
@jamesbraid
jamesbraid deleted the display-stop-deadlock branch November 1, 2026 22:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants