You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On a single-GPU run, DoDynamicsThenSync intermittently never returns. The process stays alive with
the GPU at 99–100% SM utilization, but the simulation makes no further progress — no error, no CUDA
Xid, no exception. It is a livelock, not a crash.
Onset is stochastic and concentrated in the contact-ramp regime — the moment a gravity-settled
granular pack lands on the floor and the contact count ramps sharply. In a controlled repeat of a
minimal scene (below) it wedged in 3 of 7 runs, always at chunk ~30–40 (the ramp), where the
confirmed-contact count is climbing through ~2k → ~12k; the other 4 runs completed cleanly. It also
occasionally wedges much later (I have seen it at ~chunk 1449 in a longer run).
I originally hit this with a custom JIT force model and suspected my own kernel, but it reproduces
identically with the stock UseFrictionalHertzianModel() and no custom code, so it appears to be
in the async engine itself rather than in user force code.
Correction (updated): an earlier version of this report listed the OS as WSL2. That was wrong —
this reproduces on bare-metal Linux with a native (non-virtualized) GPU. See the Environment
section. Re-confirmed on bare-metal: rolling the minimal repro below, one run wedged at chunk k=19 (nc≈2.5k, mid contact-ramp) with the GPU at 100% SM, one thread R (33% CPU) and 23
in futex_wait_queue — the exact signature below; a separate run of the same scene completed
3000/3000 chunks cleanly, i.e. the stochasticity holds off-WSL. This matters for the diagnosis: it
is not a WSL GPU-scheduling artifact, and it is therefore likely a different root cause from #70 (which is genuinely on WSL2), or a shared root cause that is platform-independent.
Environment
DEME 2.3.3 (PyPI sdist, built from source, CUDAARCHS=89)
GPU: NVIDIA RTX 2000 Ada (compute 8.9), 16 GB
Driver 580.159.03, CUDA toolkit 12.8, GCC 13.3
OS: bare-metal Ubuntu 24.04 (native GPU — not a VM, not WSL; nvidia-smi -q reports
Virtualization Mode: None, GPU on PCI 01:00.0), Python 3.12, DEMSolver(nGPUs=1)
Signature (how to recognize it)
DoDynamicsThenSync does not return; process alive.
nvidia-smi: the process at 99–100% SM, memory-controller ~0%.
Thread states (ps -L -o stat,wchan): 1–2 threads R (running), the rest (~20+) in futex_wait_queue — the kinematic thread appears to spin while the dynamics thread and workers wait.
faulthandler.dump_traceback_later(...) shows the main Python thread blocked inside DoDynamicsThenSync (a bare C-call frame; no Python-level loop).
Distinct from a GPU-idle hang: here the GPU is busy.
Minimal reproduction
Gravity-settle ~600 three-sphere clumps (sub-mm grains, the bundled clumps/3_clump.csv scaled so r_max ≈ 0.35 mm) into a 16 mm walled box, stock frictional-Hertzian model, dt = 2.66e-6, stepping in
chunks of 200 substeps. Because onset is stochastic, run it a handful of times — it wedged in ~3/7
here, typically within the first few tens of chunks (the contact ramp).
What I ruled out (each falsified by a subsequent run that behaved oppositely)
Adaptive kT update-frequency tuner — SetCDUpdateFreq(20) is initial-only; the tuner climbs it
(20 → ~37 over ~30 chunks) and the CD margin / potential-pair count grow with it (~3× at the same
stage vs a fixed frequency). DisableAdaptiveUpdateFreq() let one run complete 3000/3000 — but a
later run stalled with the tuner disabled, so it is at most an aggravator.
Adaptive bin sizing — GetBinSize()/GetBinNum() were stable up to the wedge; disabling it
changed nothing.
Host-side bulk getters racing the async protocol — a run calling GetOwnerPosition/ GetOwnerVelocity every N chunks wedged at such a call, but a fully blind run (only DoDynamicsThenSync + GetNumContacts) also wedged. And the repro above wedges with neither.
Custom force model — reproduces with the stock frictional-Hertzian model, as above.
WSL2 / GPU virtualization — reproduces on bare-metal Linux with a native GPU (see Environment),
so it is not a WSL GPU-scheduling artifact.
The through-line: no host-side call pattern reliably prevents it, which points at a race in the async
kT/dT engine, hazard concentrated at the contact ramp.
Workaround (in case it helps others landing here)
A supervisor that watchdogs stdout and, on >60 s of silence after Initialize(), kills and relaunches
the run — runs that clear the contact ramp almost always complete. Not a fix.
Questions
Is there a known race in the kT/dT handshake that a sharp contact-count increase can trigger?
Is there a supported way to run the solver fully synchronously (no async kinematic thread) as a
diagnostic, to confirm the async engine is the locus?
Summary
On a single-GPU run,
DoDynamicsThenSyncintermittently never returns. The process stays alive withthe GPU at 99–100% SM utilization, but the simulation makes no further progress — no error, no CUDA
Xid, no exception. It is a livelock, not a crash.
Onset is stochastic and concentrated in the contact-ramp regime — the moment a gravity-settled
granular pack lands on the floor and the contact count ramps sharply. In a controlled repeat of a
minimal scene (below) it wedged in 3 of 7 runs, always at chunk ~30–40 (the ramp), where the
confirmed-contact count is climbing through ~2k → ~12k; the other 4 runs completed cleanly. It also
occasionally wedges much later (I have seen it at ~chunk 1449 in a longer run).
I originally hit this with a custom JIT force model and suspected my own kernel, but it reproduces
identically with the stock
UseFrictionalHertzianModel()and no custom code, so it appears to bein the async engine itself rather than in user force code.
Environment
CUDAARCHS=89)nvidia-smi -qreportsVirtualization Mode: None, GPU on PCI
01:00.0), Python 3.12,DEMSolver(nGPUs=1)Signature (how to recognize it)
DoDynamicsThenSyncdoes not return; process alive.nvidia-smi: the process at 99–100% SM, memory-controller ~0%.ps -L -o stat,wchan): 1–2 threadsR(running), the rest (~20+) infutex_wait_queue— the kinematic thread appears to spin while the dynamics thread and workers wait.faulthandler.dump_traceback_later(...)shows the main Python thread blocked insideDoDynamicsThenSync(a bare C-call frame; no Python-level loop).Minimal reproduction
Gravity-settle ~600 three-sphere clumps (sub-mm grains, the bundled
clumps/3_clump.csvscaled sor_max ≈ 0.35 mm) into a 16 mm walled box, stock frictional-Hertzian model,dt = 2.66e-6, stepping inchunks of 200 substeps. Because onset is stochastic, run it a handful of times — it wedged in ~3/7
here, typically within the first few tens of chunks (the contact ramp).
What I ruled out (each falsified by a subsequent run that behaved oppositely)
SetCDUpdateFreq(20)is initial-only; the tuner climbs it(20 → ~37 over ~30 chunks) and the CD margin / potential-pair count grow with it (~3× at the same
stage vs a fixed frequency).
DisableAdaptiveUpdateFreq()let one run complete 3000/3000 — but alater run stalled with the tuner disabled, so it is at most an aggravator.
GetBinSize()/GetBinNum()were stable up to the wedge; disabling itchanged nothing.
GetOwnerPosition/GetOwnerVelocityevery N chunks wedged at such a call, but a fully blind run (onlyDoDynamicsThenSync+GetNumContacts) also wedged. And the repro above wedges with neither.so it is not a WSL GPU-scheduling artifact.
The through-line: no host-side call pattern reliably prevents it, which points at a race in the async
kT/dT engine, hazard concentrated at the contact ramp.
Workaround (in case it helps others landing here)
A supervisor that watchdogs stdout and, on >60 s of silence after
Initialize(), kills and relaunchesthe run — runs that clear the contact ramp almost always complete. Not a fix.
Questions
diagnostic, to confirm the async engine is the locus?
Happy to run additional instrumentation (
compute-sanitizer --tool racecheck, per-chunk timing, threaddumps) if useful.