Raised in review on #139 and deferred there, because the obvious fix is a trade rather than a fix
and wants deciding on its own.
The gap
launch_process_begin issues a CreateProcessWide that DbgEng defers to the next WaitForEvent,
and hands back a PendingTarget guard. Drop that guard before any pump and Arrivals::forget
takes its still-Waiting entry out of the register, leaving no exclusion behind — there is nothing
to leave, since the open was never delivered a process and nobody knows which one it would have got.
The create is still queued. So:
launch_process_begin("a") — entry registered, create deferred.
- the guard is dropped — entry removed, no exclusion.
launch_process_begin("b") — snapshot taken, and it cannot contain a's process, which does not
exist yet.
- one
WaitForEvent materialises a, and it is new to b's snapshot, not attached and not
named by any pending attach — so b's open claims it.
b's wait() returns Ok(()) with b's own process not yet at its initial break.
Why it is not simply "keep the entry"
That is the fix review proposed, and it closes step 4 for the ordinary case: the abandoned entry is
first in registration order, so it claims a and b waits for the next arrival.
What it does not have is anything to retire it by. A guard is what ends an entry's life, and this
one has none — so an abandoned launch that never starts leaves a registration that competes for
arrivals for the rest of the session. That case is not hypothetical: examples/deferred_arrival.rs
arm C measures a launch whose image does not exist failing inside the wait (Err(0x80070002) in
13 ms, no debuggee behind it). The ghost then takes the next launch's process and times that guard
out.
So the choice is a wrong Ok on every abandoned launch against a wrong timeout on every abandoned
launch that failed to start. The second fails closed and is rarer, which is an argument, but it is
a different bug rather than the absence of one — and it is the shape #136 stage 3 was written to
get rid of: an entry outliving its reader, with no signal that says when it is done.
Prior art in this repo
This is the residue of the ambiguity Arrival's doc comment recorded as accepted before #139
("the elimination is ambiguous for two launches pending at once"). #139 closed the two cases that
have a signal to close them with — both guards live, and delivered-then-dropped, where the claim is
inherited by the opens still waiting — and this is the third, which has none.
What would close it
Something that says an abandoned launch's create has been resolved, one way or the other. Options
not yet weighed against each other:
- retire an abandoned entry on the pump that fails, since arm C shows the failure surfaces
there — bounded, but only for the failure mode it can see;
- make
launch_process_begin non-deferred, so a dropped guard has a process to name;
- refuse to drop a launch guard silently — an explicit
abandon() that pumps once.
None is obviously right and none is urgent: every caller in this crate and in windbg-mcp waits on
the guard it is given.
Raised in review on #139 and deferred there, because the obvious fix is a trade rather than a fix
and wants deciding on its own.
The gap
launch_process_beginissues aCreateProcessWidethat DbgEng defers to the nextWaitForEvent,and hands back a
PendingTargetguard. Drop that guard before any pump andArrivals::forgettakes its still-
Waitingentry out of the register, leaving no exclusion behind — there is nothingto leave, since the open was never delivered a process and nobody knows which one it would have got.
The create is still queued. So:
launch_process_begin("a")— entry registered, create deferred.launch_process_begin("b")— snapshot taken, and it cannot containa's process, which does notexist yet.
WaitForEventmaterialisesa, and it is new tob's snapshot, not attached and notnamed by any pending attach — so
b's open claims it.b'swait()returnsOk(())withb's own process not yet at its initial break.Why it is not simply "keep the entry"
That is the fix review proposed, and it closes step 4 for the ordinary case: the abandoned entry is
first in registration order, so it claims
aandbwaits for the next arrival.What it does not have is anything to retire it by. A guard is what ends an entry's life, and this
one has none — so an abandoned launch that never starts leaves a registration that competes for
arrivals for the rest of the session. That case is not hypothetical:
examples/deferred_arrival.rsarm C measures a launch whose image does not exist failing inside the wait (
Err(0x80070002)in13 ms, no debuggee behind it). The ghost then takes the next launch's process and times that guard
out.
So the choice is a wrong
Okon every abandoned launch against a wrong timeout on every abandonedlaunch that failed to start. The second fails closed and is rarer, which is an argument, but it is
a different bug rather than the absence of one — and it is the shape #136 stage 3 was written to
get rid of: an entry outliving its reader, with no signal that says when it is done.
Prior art in this repo
This is the residue of the ambiguity
Arrival's doc comment recorded as accepted before #139("the elimination is ambiguous for two launches pending at once"). #139 closed the two cases that
have a signal to close them with — both guards live, and delivered-then-dropped, where the claim is
inherited by the opens still waiting — and this is the third, which has none.
What would close it
Something that says an abandoned launch's create has been resolved, one way or the other. Options
not yet weighed against each other:
there — bounded, but only for the failure mode it can see;
launch_process_beginnon-deferred, so a dropped guard has a process to name;abandon()that pumps once.None is obviously right and none is urgent: every caller in this crate and in windbg-mcp waits on
the guard it is given.