Severity: Info (latent — defensive callsites prevent it today)
File: src/Servy.Service/ProcessManagement/ProcessLauncher.cs, lines 94-100
The synchronous-mode timeout enforcement is gated on both TimeoutMs > 0 and the heartbeat delegate being set:
// Synchronous mode: Wait for exit while pulsing the SCM
if (options.TimeoutMs > 0 && (options.OnScmHeartbeat?.Target != null || options.OnScmHeartbeat?.Method != null))
{
WaitForExitWithHeartbeat(process, options, logger);
}
// Ensure all async reads are finished
process.UnderlyingProcess.WaitForExit();
If a caller sets TimeoutMs but does not wire up OnScmHeartbeat, the heartbeat-loop block is skipped and execution falls through to the bare WaitForExit() on line 100, which has no timeout. The configured TimeoutMs is silently dropped — the process can hang the launcher indefinitely.
Today the only callers (Service.StartPreStopProcess, Service.RunSynchronousPreLaunch) always set both, so the bug is dormant. But:
Suggested fix:
Decouple timeout enforcement from heartbeat presence. Always honor TimeoutMs when it is positive; treat heartbeat as optional inside the wait loop:
if (options.TimeoutMs > 0)
{
WaitForExitWithHeartbeat(process, options, logger); // pass null-safe heartbeat
}
else
{
process.UnderlyingProcess.WaitForExit();
}
And inside WaitForExitWithHeartbeat, guard the pulse: options.OnScmHeartbeat?.Invoke(options.ScmAdditionalTimeMs); already does the right thing — the existing code is fine if the gate is loosened.
Severity: Info (latent — defensive callsites prevent it today)
File:
src/Servy.Service/ProcessManagement/ProcessLauncher.cs, lines 94-100The synchronous-mode timeout enforcement is gated on both
TimeoutMs > 0and the heartbeat delegate being set:If a caller sets
TimeoutMsbut does not wire upOnScmHeartbeat, the heartbeat-loop block is skipped and execution falls through to the bareWaitForExit()on line 100, which has no timeout. The configuredTimeoutMsis silently dropped — the process can hang the launcher indefinitely.Today the only callers (
Service.StartPreStopProcess,Service.RunSynchronousPreLaunch) always set both, so the bug is dormant. But:Target/Methodnull check; if that simplification lands without addressing this, the bug stays dormant in a fresh form.Suggested fix:
Decouple timeout enforcement from heartbeat presence. Always honor
TimeoutMswhen it is positive; treat heartbeat as optional inside the wait loop:And inside
WaitForExitWithHeartbeat, guard the pulse:options.OnScmHeartbeat?.Invoke(options.ScmAdditionalTimeMs);already does the right thing — the existing code is fine if the gate is loosened.