fix(frame-timing): keep the GC monitor quiet at interpreter shutdown (#734)

A collection during interpreter shutdown called GcMonitor after the
module's `time` global was torn down, printing "Exception ignored while
calling GC callback ... 'NoneType' object has no attribute
'perf_counter'" at the end of service and test runs.

- GcMonitor binds its clock and sys.is_finalizing at construction and
  does nothing once the interpreter is finalizing.
- install_gc_monitor() unregisters it with atexit; new
  uninstall_gc_monitor().
- DisplayManager.cleanup() (reached from SIGTERM via run()'s finally)
  unregisters it alongside the frame recorder.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-10-03 11:51:06 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent a5ec645d25
commit 76ad71d1c5
5 changed files with 133 additions and 5 deletions
+12
View File
@@ -118,6 +118,18 @@ class TestDisplayManagerResourceManagement:
dm.matrix.Clear.assert_called()
def test_cleanup_takes_the_gc_monitor_out_of_gc_callbacks(
self, test_config, mock_rgb_matrix):
"""The service stops (SIGTERM -> run()'s finally -> cleanup()) with
its collection timer unregistered, not left for interpreter teardown."""
import gc
with patch.dict('os.environ', {'EMULATOR': 'false'}):
dm = DisplayManager(test_config)
monitor = dm.frame_timing.gc_monitor
assert monitor in gc.callbacks
dm.cleanup()
assert monitor not in gc.callbacks
class TestDisplayManagerDoubleSided:
"""Double-sided mode: render once at logical size, tile across the chain."""