B2.3: migrate MGC to LatestWinsGate.fireAndWait
specificClass.js 319 → 311 lines. Removed inline _dispatchInFlight +
_delayedCall + finally block. handleInput is now a 1-line delegate
to DemandDispatcher.fireAndWait({source, demand, ...}).
turnOffAllMachines calls _demandDispatcher.cancelPending().
DemandDispatcher 39 → 53 lines. One integration test rewritten to
use the new sentinel-resolution semantics. 77/77 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -116,16 +116,27 @@ test('repeated turnOffAllMachines reaches idle (serializes concurrent shutdowns)
|
||||
'delayedMove must be cleared after shutdown');
|
||||
});
|
||||
|
||||
test('turnOffAllMachines clears MGC._delayedCall to cancel any deferred dispatch', async () => {
|
||||
test('turnOffAllMachines cancels any parked demand so it cannot re-engage pumps', async () => {
|
||||
// PS sends a 1% keep-alive while MGC is mid-dispatch. MGC parks it in
|
||||
// _delayedCall. PS then crosses stopLevel and calls turnOffAllMachines.
|
||||
// Without clearing _delayedCall, MGC's finally block fires the parked
|
||||
// 1% call AFTER the shutdown — re-engaging the pump.
|
||||
// its demand dispatcher's latest-wins slot. PS then crosses stopLevel
|
||||
// and calls turnOffAllMachines. Without cancelPending(), the parked
|
||||
// 1% call would fire AFTER the shutdown — re-engaging the pump.
|
||||
const { mgc } = buildGroup();
|
||||
mgc._delayedCall = { source: 'parent', demand: 1, powerCap: Infinity, priorityList: null };
|
||||
const gate = mgc._demandDispatcher._gate;
|
||||
// Pin a fake in-flight dispatch then park a pending call behind it.
|
||||
gate._inFlight = true;
|
||||
const parked = mgc.handleInput('parent', 1, Infinity, null);
|
||||
|
||||
await mgc.turnOffAllMachines();
|
||||
|
||||
assert.equal(mgc._delayedCall, null,
|
||||
'turnOff must cancel any deferred dispatch so it cannot re-engage pumps post-shutdown');
|
||||
// Re-open the gate: the in-flight pin is artificial. Awaiting the
|
||||
// parked promise must yield the SUPERSEDED sentinel (i.e. it was
|
||||
// cancelled, not run).
|
||||
const res = await parked;
|
||||
assert.ok(res && res.superseded === true,
|
||||
'parked demand must resolve as superseded after turnOffAllMachines cancels it');
|
||||
// Idle now — pending slot must be clear.
|
||||
assert.equal(gate._pending, null,
|
||||
'turnOff must cancel any parked demand so it cannot re-engage pumps post-shutdown');
|
||||
gate._inFlight = false;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user