Launch pend counter: no timeout, no queue entry (mid-load launch crash 2)
The take-1 deferral (re-post at +1s, capped at 120) turned a SLOW load into a crash: loads on a busy operator box measure 1-2+ minutes, the cap expired, and the original "Not ready to run" Fail/abort fired (field report: sound after ~1 min, crash ~2 min later; the log shows exactly 120 deferrals all in LoadingMission). A mid-load RunMission now just bumps gBTRunMissionPendCount -- nothing enters the event queue and there is NO timeout to outlive -- and CheckLoadMessageHandler drains the counter immediately after advancing LoadingMission -> WaitingForLaunch, re-posting that many stack RunMissionMessages (the engine's own no-console launch pattern at the same site, which also demonstrates Post spools stack messages safely). Verified (two-round rig, round 1): both relay launches pended through the load, fired at load completion, the requested mech spawned and the scene went live. Round reset + round-2 rejoin remain verified from the prior run; why loads take 1-2+ min on a busy box is left as an open perf question. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3a9b35fc06
commit
53c86fa1dd
@@ -425,6 +425,18 @@ phase). Plan: `~/.claude/plans/partitioned-snuggling-piglet.md`.
|
||||
deferrals so a wedged load still surfaces the original Fail. `[launch] ... deferred +1s`
|
||||
log per deferral. Verified: the crashing shape deferred 104x through a ~50s load, then
|
||||
launched and ran clean.
|
||||
- **LAUNCH/LOAD RACE take 2 -- the PEND COUNTER (2026-07-22) [T2]**: the take-1 deferral
|
||||
(re-post at +1s, 120 cap) turned a SLOW load into a crash -- loads on a busy operator box
|
||||
measured 1-2+ minutes, the cap expired, and the original Fail/abort fired (field-reported:
|
||||
"sound after 1 min, crash 2 min later", exactly 120 deferrals in the log). Now a mid-load
|
||||
RunMission just bumps `gBTRunMissionPendCount` (L4APP.cpp) -- NOTHING enters the event queue,
|
||||
NO timeout -- and APP.cpp `CheckLoadMessageHandler` drains the counter right after it advances
|
||||
LoadingMission->WaitingForLaunch, re-posting that many stack RunMissions (the engine's own
|
||||
no-console launch pattern at the same site). Verified: both relay launches pended through the
|
||||
load, fired at completion, mech spawned + scene LIVE. NOTE the queue-blocking livelock theory
|
||||
from take 2's commit is UNPROVEN -- the measured fact is just that loads outlive any fixed
|
||||
cap; the counter design is correct under either reading. Residual: WHY loads take 1-2+ min
|
||||
on the operator box (vs ~30s earlier same day) is an open perf question ([loadobj] timings).
|
||||
- **ROUND RESET (2026-07-22) [T2]**: trim/prefs FINALIZE the egg for one round -- and that used
|
||||
to be permanent for the relay's lifetime, so a REJOIN after a mission (or crash) was served
|
||||
the stale finalized egg and its new callsign/mech request was recorded but never applied (the
|
||||
|
||||
@@ -1415,6 +1415,18 @@ void
|
||||
Post(DefaultEventPriority, this, &run_mission_message);
|
||||
Tell("Sent ready message to ourselves\n");
|
||||
}
|
||||
|
||||
{
|
||||
// relay launches that arrived MID-LOAD fire now (they could
|
||||
// not enter the queue then -- see L4APP.cpp pend counter)
|
||||
extern int BTTakePendingRunMissions(void);
|
||||
int pended_launches = BTTakePendingRunMissions();
|
||||
while (pended_launches-- > 0)
|
||||
{
|
||||
RunMissionMessage pended_run;
|
||||
Post(DefaultEventPriority, this, &pended_run);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
+36
-25
@@ -564,6 +564,23 @@ Entity*
|
||||
return viewing_entity;
|
||||
}
|
||||
|
||||
//
|
||||
// Pended mid-load operator launches (see the handler's default case).
|
||||
// APP.cpp CheckLoadMessageHandler drains the count the moment the state
|
||||
// reaches WaitingForLaunch and re-posts that many RunMissions.
|
||||
//
|
||||
int gBTRunMissionPendCount = 0;
|
||||
|
||||
int BTTakePendingRunMissions(void)
|
||||
{
|
||||
int pended = gBTRunMissionPendCount;
|
||||
gBTRunMissionPendCount = 0;
|
||||
if (pended > 0)
|
||||
DEBUG_STREAM << "[launch] load complete -- firing " << pended
|
||||
<< " pended RunMission(s)" << std::endl << std::flush;
|
||||
return pended;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// RunMissionMessageHandler
|
||||
@@ -611,31 +628,25 @@ void
|
||||
|
||||
default:
|
||||
//
|
||||
// LAUNCH/LOAD RACE (2026-07-22): the relay sends the RunMission pair
|
||||
// a fixed settle after the egg ACK, but a long mission load (glass +
|
||||
// dev gauges on a busy box) can still be in LoadingMission when it
|
||||
// lands -- the 1995 console never hit this state (the operator
|
||||
// launched much later), so it Fail()ed = the "silent crash at
|
||||
// mission launch" (c0000409 abort; 3/3 reproduced under cdb).
|
||||
// DEFER instead: re-post the message to ourselves at +1s until the
|
||||
// ladder reaches WaitingForLaunch (the VehicleDead re-post pattern).
|
||||
// Bounded so a truly wedged load still surfaces the original Fail.
|
||||
{
|
||||
static int s_launch_deferrals = 0;
|
||||
if (++s_launch_deferrals <= 120)
|
||||
{
|
||||
DEBUG_STREAM << "[launch] RunMission arrived in state "
|
||||
<< (int)GetApplicationState()
|
||||
<< " (still loading) -- deferred +1s ("
|
||||
<< s_launch_deferrals << ")" << std::endl << std::flush;
|
||||
Time when = Now();
|
||||
when += 1.0f;
|
||||
Post(HighEventPriority, this, message, when);
|
||||
return; // base handler runs when ready
|
||||
}
|
||||
}
|
||||
Fail("Application::RunMissionMessageHandler - Not ready to run!\n");
|
||||
break;
|
||||
// LAUNCH/LOAD RACE, take 2 (2026-07-22): the relay's RunMission pair
|
||||
// can land while the pod is still in LoadingMission (long loads; the
|
||||
// 1995 console launched operator-paced, so this state Fail()ed = the
|
||||
// silent c0000409 crash). Take 1 re-posted the message at +1s --
|
||||
// but the LoadingMission -> WaitingForLaunch transition
|
||||
// (APP.cpp CheckLoadMessageHandler) requires
|
||||
// IsPriorityEmpty(MinEventPriority), so the pending deferred launch
|
||||
// KEPT THE QUEUE NON-EMPTY and blocked the very transition it waited
|
||||
// for (livelock -> 120 x 1s cap -> the same abort; field-reported).
|
||||
// Now a mid-load launch just bumps a COUNTER -- nothing enters the
|
||||
// event queue -- and CheckLoadMessageHandler re-posts the launches
|
||||
// (the engine's own no-console stack-message pattern) right after it
|
||||
// advances the state.
|
||||
++gBTRunMissionPendCount;
|
||||
DEBUG_STREAM << "[launch] RunMission arrived in state "
|
||||
<< (int)GetApplicationState()
|
||||
<< " (still loading) -- pended until the load completes ("
|
||||
<< gBTRunMissionPendCount << ")" << std::endl << std::flush;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user