Windows: a failed console takeover leaves the runner's console state unrestored
Summary
On Windows the legacy strategy takes over the console of the job process before it signals that process. The takeover is three steps, and each step returns early when it fails, before the block that puts the runner's console back:
Two of those early returns leave the runner in a changed console state:
attachConsolefails afterfreeConsolesucceeded. The runner then holds no console at all.setConsoleCtrlHandlerfails after the first two succeeded. The runner then holds the console of the job process, with its own Ctrl-C handler still enabled.
The restore block sits after the delivery call, so neither path reaches it. The state persists after the function returns.
Impact
A console is process-wide, so the state outlives the job that caused it:
- A runner left with no console cannot raise a control event for any later job, because
GenerateConsoleCtrlEventneeds the caller to hold one. That is the symptom of #39297 (closed), reached from a different direction. One failed takeover degrades every later cancellation on that runner to a force kill. - A runner left on the console of a terminated job holds a console that goes away with that process.
Severity is low. Both paths need the takeover to fail partway, which no report describes. The defect is that the code has no way back to a known console state once a takeover starts.
Proposal
Restore the console on every exit from the takeover, not only on the successful one. Undo the steps that already succeeded, and keep reporting the failure to the caller so it still force-kills.
Scope
Testable through the consoleAPI seam that !7275 (merged) adds. No Windows service and no new CI infrastructure needed.
Notes
Found while reviewing !7275 (merged). That merge request keeps a failed console restore from force-killing a healthy job, which is a separate concern, so this gap was left out of it.