From 7f4102b25b9f67642b0a77c358d5edf40b60e5fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 15:53:07 +0000 Subject: [PATCH 1/2] Initial plan From 82b5e40b4384c25e8df3f7890f02b5f31fdd6605 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:01:14 +0000 Subject: [PATCH 2/2] Implement CodeRabbit suggestions for shutdown improvements Co-authored-by: myieye <12587509+myieye@users.noreply.github.com> --- backend/FwLite/FwLiteWeb/Program.cs | 14 ++++--- platform.bible-extension/src/main.ts | 60 +++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/backend/FwLite/FwLiteWeb/Program.cs b/backend/FwLite/FwLiteWeb/Program.cs index 55020254a..bc5304633 100644 --- a/backend/FwLite/FwLiteWeb/Program.cs +++ b/backend/FwLite/FwLiteWeb/Program.cs @@ -15,14 +15,16 @@ var url = app.Urls.First(); LocalAppLauncher.LaunchBrowser(url); } - //windows is dumb and so you can't send SIGINT to a process, so we need to listen for a shutdown command + // Windows can't receive SIGINT as a child; listen for a "shutdown" command on stdin. _ = Task.Run(async () => - { - // Wait for the "shutdown" command from stdin - while (await Console.In.ReadLineAsync() is not "shutdown") { } + { + string? line; + while ((line = await Console.In.ReadLineAsync()) is not null + && !string.Equals(line.Trim(), "shutdown", StringComparison.OrdinalIgnoreCase)) + { /* keep waiting */ } - await app.StopAsync(); - }); + await app.StopAsync(); + }); await app.WaitForShutdownAsync(); } diff --git a/platform.bible-extension/src/main.ts b/platform.bible-extension/src/main.ts index 630e0af56..e2f2e0caf 100644 --- a/platform.bible-extension/src/main.ts +++ b/platform.bible-extension/src/main.ts @@ -293,10 +293,30 @@ function launchFwLiteFwLiteWeb(context: ExecutionActivationContext) { return { baseUrl, fwLiteProcess }; } -function shutdownFwLite(fwLiteProcess: ReturnType['fwLiteProcess']): Promise { - return new Promise((resolve, _) => { +function shutdownFwLite( + fwLiteProcess: ReturnType['fwLiteProcess'], +): Promise { + return new Promise((resolve) => { + // Already exited? + // eslint-disable-next-line no-null/no-null + if (fwLiteProcess.exitCode !== null) { + resolve(fwLiteProcess.exitCode === 0); + return; + } + + let forced = false; + const killTimer = setTimeout(() => { + forced = true; + try { + fwLiteProcess.kill('SIGKILL'); + } catch (e) { + logger.error(`[FwLiteWeb]: forced kill failed: ${e}`); + } + }, 10000); + fwLiteProcess.once('exit', (code, signal) => { - if (code === 0) { + clearTimeout(killTimer); + if (code === 0 || forced) { resolve(true); } else { logger.error(`[FwLiteWeb]: shutdown failed with code '${code}', signal '${signal}'`); @@ -304,15 +324,37 @@ function shutdownFwLite(fwLiteProcess: ReturnType[ } }); fwLiteProcess.once('error', (error) => { + clearTimeout(killTimer); logger.error(`[FwLiteWeb]: shutdown failed with error: ${error}`); resolve(false); }); - fwLiteProcess.stdin.write('shutdown\n'); - fwLiteProcess.stdin.end(); - setTimeout(() => { - fwLiteProcess.kill('SIGKILL'); - resolve(true); - }, 10000); + // Guard stdin operations + if (fwLiteProcess.stdin && !fwLiteProcess.stdin.destroyed) { + try { + fwLiteProcess.stdin.write('shutdown\n'); + fwLiteProcess.stdin.end(); + } catch (e) { + logger.error(`[FwLiteWeb]: stdin write failed: ${e}`); + // Fall back to immediate forced kill + forced = true; + clearTimeout(killTimer); + try { + fwLiteProcess.kill('SIGKILL'); + } catch (killError) { + logger.error(`[FwLiteWeb]: forced kill failed: ${killError}`); + } + } + } else { + // No stdin available, force kill immediately + logger.warn('[FwLiteWeb]: stdin unavailable, forcing immediate kill'); + forced = true; + clearTimeout(killTimer); + try { + fwLiteProcess.kill('SIGKILL'); + } catch (e) { + logger.error(`[FwLiteWeb]: forced kill failed: ${e}`); + } + } }); }