Signal jobs through their own console on Windows services
GenerateConsoleCtrlEvent needs the calling process to hold a console. A runner that runs as a Windows service holds none, so cancelling a job always failed with an invalid handle and the job process was killed rather than asked to stop. The job process is a console application, so Windows gives it a console of its own, and that console can be borrowed to reach it.
- Fall back to the console of the job process when the direct event fails.
- Keep the direct path first, so a runner started from a console is unaffected.
- Guard the console swaps with a mutex, since
FreeConsoleandAttachConsoleapply to the whole runner process. - Tolerate a failed detach in the fallback, because a runner with no console has nothing to detach from.
- Add Windows unit tests for each fallback outcome.
The job process is created with CREATE_NEW_PROCESS_GROUP, so the Ctrl-Break event reaches only that group and leaves the runner alone.
Relates to #39297 (closed)
Verification
The unit tests cover the call sequence and the error handling of each fallback outcome.
The fallback itself is covered end to end by TestKillerWithoutConsole, added in !7277 (closed). That test starts the terminating process with DETACHED_PROCESS, two levels deep, so it holds no console and its parent holds none either. It asserts that the process under test kept no console, that the job process exited with its graceful stop code, and that the fallback warning appeared, so the direct path cannot satisfy it by accident. It passes on Windows 1809 and 21H2, for both process strategies.
Not covered: session 0. A service runs there, a detached process does not. The console state is faithful, and every symptom on #39297 (closed) follows from the console state, so confirmation on a real service install is still welcome.
The reasoning rests on one observation from that issue: AttachConsole against the job process succeeds even when the runner runs as a service, which means the job process has a console of its own.
Targets 39297-windows-terminate-cleanup-errors. Without that change the restore failure in this new path would still trigger the immediate force-kill.