Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions backend/FwLite/FwLiteWeb/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
60 changes: 51 additions & 9 deletions platform.bible-extension/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,26 +293,68 @@ function launchFwLiteFwLiteWeb(context: ExecutionActivationContext) {
return { baseUrl, fwLiteProcess };
}

function shutdownFwLite(fwLiteProcess: ReturnType<typeof launchFwLiteFwLiteWeb>['fwLiteProcess']): Promise<boolean> {
return new Promise((resolve, _) => {
function shutdownFwLite(
fwLiteProcess: ReturnType<typeof launchFwLiteFwLiteWeb>['fwLiteProcess'],
): Promise<boolean> {
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}'`);
resolve(false);
}
});
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}`);
}
}
});
}