mirror of
https://github.com/XuehaiPan/nvitop.git
synced 2026-10-23 08:07:34 -06:00
[GH-ISSUE #222] [BUG] Intermittent segfault on quit: atexit nvmlShutdown() races with un-joined snapshot daemon threads #216
Labels
No labels
api
bug
bug
cli / tui
dependencies
documentation
documentation
documentation
duplicate
enhancement
exporter
invalid
pull-request
pynvml
question
question
upstream
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
github-starred/nvitop#216
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @zengchang233 on GitHub (Jul 9, 2026).
Original GitHub issue: https://github.com/XuehaiPan/nvitop/issues/222
Originally assigned to: @XuehaiPan on GitHub.
Required prerequisites
What version of nvitop are you using?
1.7.0
Operating system and environment
Problem description
Pressing q to quit the TUI intermittently crashes with:
dmesgshows the faulting thread is one of the snapshot daemons, dying inside libc/libnvidia-ml with a near-NULL dereference:Root cause analysis
nvitop.api.libnvmlregisters an atexit hook on first NVML use:https://github.com/XuehaiPan/nvitop/blob/v1.7.0/nvitop/api/libnvml.py#L275-L276
Meanwhile the TUI panels (
DevicePanel,HostPanel,ProcessPanel) each run a snapshot daemon thread (device-snapshot-daemon, etc.). On quit,destroy()only callsself._daemon_running.clear()— the threads are neverjoin()ed:https://github.com/XuehaiPan/nvitop/blob/v1.7.0/nvitop/tui/screens/main/panels/device.py#L531-L533
A daemon thread sleeping in
time.sleep(SNAPSHOT_INTERVAL)or mid-take_snapshots()doesn't observe the cleared event before the main thread reaches interpreter finalization. The atexit hook then callsnvmlShutdown()while the snapshot thread is still inside alibnvidia-mlcall → use-after-free of NVML internal state → SIGSEGV. The race window is a few milliseconds, so the crash is intermittent (for me: roughly 1 in 3 quits on an otherwise idle 8-GPU box).Steps to Reproduce
The race is hard to hit through the TUI deterministically, but the underlying mechanism reproduces reliably:
Results on my machine: 6/15 runs exit with rc=139 (SIGSEGV).
Adding
atexit.unregister(nvitop.api.libnvml.nvmlShutdown)before exit: 0/15 crashes (15/15 clean).Commenting out the
_atexit.register(nvmlShutdown)line inlibnvml.pyalso fixes quitting the real TUI (verified with repeated pty-drivennvitop→qcycles).Expected Behavior
nvitopexits cleanly on q without a segmentation fault.Additional context
Possible fixes, in order of decreasing safety:
nvmlShutdownentirely. The OS reclaims NVML resources at process exit; an explicit shutdown at interpreter finalization buys nothing but this race.destroy()(requires interruptible sleep, e.g.Event.wait(interval)instead oftime.sleep(interval), so join is bounded).nvmlShutdown()with a shared lock — likely too coarse given snapshot frequency.Happy to send a PR for option 1 or 2 if you have a preference.
@XuehaiPan commented on GitHub (Jul 10, 2026):
Hi @zengchang233, thanks for the detailed report and spot-on diagnosis. The cause is that
atexitnvmlShutdown()freed NVML state while a background thread was still mid-libnvidia-mlcall (use-after-free).I have fixed this in PR #223:
nvmlQuerydrains in-flight NVML calls before theatexitshutdown, so NVML is never torn down under a live call. Verified with yournvml_race.pyon a 4×H100 host —main~15/25 segfaults, fixed 0/25.It'll ship in the next release; you can test against
mainnow. Thanks for the clean reproducer!examples/folder and relocate the deprecatednvitop.callbacks#219