[Bugfix] The Gauntlet can OOM / hard-lock the machine — GRID TITAN ramps grid side unbounded #353
Labels
No labels
Alpha Release Requirement
Bugfix
Demo Target
Documentation
Major Feature
Minor Feature
priority:tier1-active
priority:tier2-foundation
priority:tier3-future
priority:tier4-deferred
Refactoring & Cleanup
system:animation
system:documentation
system:grid
system:input
system:performance
system:procgen
system:python-binding
system:rendering
system:ui-hierarchy
Tiny Feature
workflow:blocked
workflow:needs-benchmark
workflow:needs-documentation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
john/McRogueFace#353
Loading…
Add table
Add a link
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?
Summary
Running
tests/benchmarks/gauntlet/run_gauntlet.pywindowed hard-locked the desktop (2026-07-11): the machine consumed all RAM during grid allocation, could not switch to a tty, and forced a logout. Root cause: the auto-ramp has a step ceiling but no memory ceiling.Root cause
scoring.pyramps load geometrically:load_k = round(base * growth**k), capped atMAX_STEPS = 40. For GRID TITAN the load is the grid sideS(base_load=20,growth=1.4), and cost/allocation scales as S² (cells, TileLayer int32, ColorLayer RGBA, TCOD map).Because that trial renders into a fixed viewport (
zoom = VIEW_PX/(S*16)), the whole grid stays on-screen at shrinking zoom, so frame time can stay under budget while S grows. The ramp therefore keeps passing and climbing:MAX_STEPS=40would nominally reachS≈14,000,000— the OOM-killer / swap-thrash hits ~step 20-22 first, locking the machine.Fix (implemented)
Defense in depth, weakest failure mode last (
tests/benchmarks/gauntlet/safety.py):Trial.predict_bytes(load)— a trial that can estimate its footprint refuses a load whose predicted allocation exceeds a 512 MB budget before allocating it. GRID TITAN implements it (S²·~28 B) and declaresmax_load = 4300(≈494 MB at the cap).Trial.max_load— absolute hard cap on load; the ramp never sets a load above it.set_load, if resident memory exceeds a ceiling (default 1500 MB,MCRF_GAUNTLET_RSS_MB) the ramp stops gracefully.RLIMIT_ASaddress-space cap —run_gauntlet.pyinstalls a hard cap (default 2500 MB,MCRF_GAUNTLET_AS_MB) at startup. Verified: under the cap a runawayGrid()throwsstd::bad_alloc → abort(clean process death, exit 134) instead of taking the machine down. This is the machine-saver backstop, not a graceful path.RampControllerrecords astop_reason(budget/hard_cap/max_load/mem_predict/mem_rss/max_steps).Test:
tests/unit/gauntlet_safety_test.pyproves the ramp bails on the memory guards before over-allocating (via a synthetic runaway trial that records the largest load it was asked to build), and exercises the RSS/address-space probes.Note
Also observed and fixed a separate #351 render regression during the same run (turn-manager entity moves not repainting) — committed separately (addresses #351).