Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

複数エンジン対応: DEFAULT_ENGINE_INFOSに登録されたエンジンのプロセスをすべて起動する(Promise版) #750

Merged
Merged
Changes from 24 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a135eff
add runEngineAll
aoirint Mar 8, 2022
f81a774
fix log
aoirint Mar 8, 2022
22e0168
fix log
aoirint Mar 8, 2022
c5202dd
add killEngineAll
aoirint Mar 8, 2022
6e46ad9
fix log
aoirint Mar 8, 2022
31bfe9f
fix log
aoirint Mar 8, 2022
eb919bd
add restartEngineAll
aoirint Mar 8, 2022
39b81df
call killEngineAll
aoirint Mar 8, 2022
c8a7c22
fix log unit
aoirint Mar 8, 2022
9778831
npm run fmt
aoirint Mar 8, 2022
2102710
assume to be killed
aoirint Mar 10, 2022
c24c7e4
add memo comment
aoirint Mar 10, 2022
5c1cf7c
fix comment
aoirint Mar 10, 2022
086bd30
call onAllKilled after final process kill errored
aoirint Mar 10, 2022
9399dd1
fix log
aoirint Mar 10, 2022
db81c2b
fix log
aoirint Mar 10, 2022
d53c4e5
rewrite engine process kill logic to use promise
aoirint Mar 10, 2022
84f350d
add comment
aoirint Mar 10, 2022
fb97d4a
remove redundant comment
aoirint Mar 10, 2022
f421c36
npm run fmt
aoirint Mar 10, 2022
26e4b2d
Merge remote-tracking branch 'origin/main' into patch-multiple-engine…
aoirint Mar 11, 2022
b681ec4
fix infinite loop when using external engine
aoirint Mar 11, 2022
2f20215
Merge branch 'patch-multiple-engine-process-container' into patch-mul…
aoirint Mar 11, 2022
45df811
Merge remote-tracking branch 'upstream/main' into patch-multiple-engi…
aoirint Mar 11, 2022
359d4e4
Merge remote-tracking branch 'upstream/main' into patch-multiple-engi…
aoirint Jun 26, 2022
acc0b02
use promise.resolve/reject and undefined to return killing status
aoirint Jun 26, 2022
eec0d02
return undefined if engine already killed
aoirint Jun 26, 2022
b8704d6
shrink code
aoirint Jun 26, 2022
c6dce69
rename var
aoirint Jun 26, 2022
1bdf974
killEngineAll now returns Record<string, Promise<void>> to avoid type…
aoirint Jun 26, 2022
5ecc203
asynchronized each engine process kill
aoirint Jun 26, 2022
5ad4780
add comment
aoirint Jun 26, 2022
3fceda1
strip redundant new promise
aoirint Jun 26, 2022
0dd3893
Update src/background.ts
Hiroshiba Jul 23, 2022
2280ae9
Update src/background.ts
Hiroshiba Jul 23, 2022
087d473
Merge remote-tracking branch 'upstream/main' into patch-multiple-engi…
aoirint Aug 10, 2022
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
239 changes: 134 additions & 105 deletions src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,114 +455,125 @@ async function runEngine(engineKey: string) {
});
}

function killEngineAll({
onFirstKillStart,
onAllKilled,
onError,
}: {
onFirstKillStart?: VoidFunction;
onAllKilled?: VoidFunction;
onError?: (engineKey: string, message: unknown) => void;
}) {
let anyKillStart = false;

const numEngineProcess = Object.keys(engineProcessContainers).length;
let numEngineProcessKilled = 0;
type KillEngineProcessResult = {
engineKey: string;
succeeded: boolean;
error?: unknown;
};

for (const [engineKey] of Object.entries(engineProcessContainers)) {
killEngine({
engineKey,
onKillStart: () => {
if (!anyKillStart) {
anyKillStart = true;
onFirstKillStart?.();
}
},
onKilled: () => {
numEngineProcessKilled++;
log.info(
`ENGINE ${numEngineProcessKilled} / ${numEngineProcess} processes killed`
);
type KillEngineProcessReturnVal = {
processKillStarted: boolean;
processKillPromise: Promise<KillEngineProcessResult>;
};

if (numEngineProcessKilled === numEngineProcess) {
onAllKilled?.();
}
},
onError: (message) => {
onError?.(engineKey, message);
type KillEngineProcessAllReturnVal = {
anyProcessKillStarted: boolean;
processKillPromises: Promise<KillEngineProcessResult>[];
};

// エディタを終了するため、エラーが起きてもエンジンプロセスをキルできたとみなして次のエンジンプロセスをキルする
numEngineProcessKilled++;
log.info(
`ENGINE ${engineKey}: process kill errored, but assume to have been killed`
);
log.info(
`ENGINE ${numEngineProcessKilled} / ${numEngineProcess} processes killed`
);
function killEngineAll(): KillEngineProcessAllReturnVal {
let anyProcessKillStarted = false;

if (numEngineProcessKilled === numEngineProcess) {
onAllKilled?.();
}
},
});
const processKillPromises: Promise<KillEngineProcessResult>[] = [];

for (const [engineKey] of Object.entries(engineProcessContainers)) {
const { processKillStarted, processKillPromise } = killEngine(engineKey);

anyProcessKillStarted = anyProcessKillStarted || processKillStarted;
processKillPromises.push(processKillPromise);
}
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
}

function killEngine({
engineKey,
onKillStart,
onKilled,
onError,
}: {
engineKey: string;
onKillStart?: VoidFunction;
onKilled?: VoidFunction;
onError?: (error: unknown) => void;
}) {
// この関数では、呼び出し元に結果を通知するためonKilledまたはonErrorを同期または非同期で必ず呼び出さなければならない
return {
anyProcessKillStarted,
processKillPromises,
};
}

function killEngine(engineKey: string): KillEngineProcessReturnVal {
const engineProcessContainer = engineProcessContainers[engineKey];
if (!engineProcessContainer) {
onError?.(`No such engineProcessContainer: key == ${engineKey}`);
return;
log.error(`No such engineProcessContainer: key == ${engineKey}`);

return {
processKillStarted: false,
processKillPromise: new Promise((resolve) =>
resolve({
engineKey,
succeeded: true,
})
),
};
}

Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
const engineProcess = engineProcessContainer.engineProcess;
if (engineProcess == undefined) {
// nop if no process started (already killed or not started yet)
log.info(`ENGINE ${engineKey}: Process not started`);
onKilled?.();
return;

return {
processKillStarted: false,
processKillPromise: new Promise((resolve) =>
resolve({
engineKey,
succeeded: true,
})
),
};
}

// considering the case that ENGINE process killed after checking process status
engineProcess.once("close", () => {
log.info(`ENGINE ${engineKey}: Process closed`);
onKilled?.();
});
const engineNotExited = engineProcess.exitCode === null;
const engineNotKilled = engineProcess.signalCode === null;

log.info(
`ENGINE ${engineKey}: last exit code: ${engineProcess.exitCode}, signal: ${engineProcess.signalCode}`
);

const engineNotExited = engineProcess.exitCode === null;
const engineNotKilled = engineProcess.signalCode === null;

if (engineNotExited && engineNotKilled) {
log.info(`ENGINE ${engineKey}: Killing process (PID=${engineProcess.pid})`);
onKillStart?.();

engineProcessContainer.willQuitEngine = true;
try {
engineProcess.pid != undefined && treeKill(engineProcess.pid);
} catch (error: unknown) {
log.error(`ENGINE ${engineKey}: Error during killing process`);
onError?.(error);
}
} else {
const isAlive = engineNotExited && engineNotKilled;
if (!isAlive) {
log.info(`ENGINE ${engineKey}: Process already closed`);
onKilled?.();

return {
processKillStarted: false,
processKillPromise: new Promise((resolve) =>
resolve({
engineKey,
succeeded: true,
})
),
};
}

return {
processKillStarted: true,
processKillPromise: new Promise((resolve) => {
log.info(
`ENGINE ${engineKey}: Killing process (PID=${engineProcess.pid})`
);

// エラーダイアログを抑制
engineProcessContainer.willQuitEngine = true;

// プロセス終了時のイベントハンドラ
engineProcess.once("close", () => {
log.info(`ENGINE ${engineKey}: Process closed`);
resolve({
engineKey,
succeeded: true,
});
});
aoirint marked this conversation as resolved.
Show resolved Hide resolved

try {
engineProcess.pid != undefined && treeKill(engineProcess.pid);
} catch (error: unknown) {
log.error(`ENGINE ${engineKey}: Error during killing process`);
resolve({
engineKey,
succeeded: false,
error,
});
}
}),
};
}

async function restartEngineAll() {
Expand Down Expand Up @@ -1173,31 +1184,49 @@ app.on("before-quit", (event) => {
return;
}

let anyKillStart = false;

log.info("Checking ENGINE status before app quit");
killEngineAll({
onFirstKillStart: () => {
anyKillStart = true;

// executed synchronously to cancel before-quit event
log.info("Interrupt app quit to kill ENGINE processes");
event.preventDefault();
},
onAllKilled: () => {
// executed asynchronously
if (anyKillStart) {
log.info("All ENGINE process killed. Quitting app");
app.quit(); // attempt to quit app again
const { anyProcessKillStarted, processKillPromises } = killEngineAll();

// すべてのエンジンプロセスが停止している
if (!anyProcessKillStarted) {
log.info("All ENGINE processes killed. Now quit app");
return;
}

// すべてのエンジンプロセスのキルを開始

// 同期的にbefore-quitイベントをキャンセル
log.info("Interrupt app quit to kill ENGINE processes");
event.preventDefault();

const numEngineProcess = processKillPromises.length; // assert == engineProcessContainers.length
let numEngineProcessKilled = 0;

// 非同期的にすべてのエンジンプロセスをキル
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
(async () => {
for (const processKillPromise of processKillPromises) {
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
const { engineKey, succeeded, error } = await processKillPromise;

if (!succeeded) {
log.error(
`ENGINE ${engineKey}: Error during killing process: ${error}`
);
// エディタを終了するため、エラーが起きてもエンジンプロセスをキルできたとみなす
}
// else: before-quit event is not cancelled
},
onError: (engineKey, message) => {
log.error(
`ENGINE ${engineKey}: Error during killing process: ${message}`

numEngineProcessKilled++;
log.info(
`ENGINE ${engineKey}: Process killed. ${numEngineProcessKilled} / ${numEngineProcess} processes killed`
);
},
});
}

// アプリケーションの終了を再試行する
log.info(
"All ENGINE process kill operations done. Attempting to quit app again"
);
app.quit();
})();
});

app.on("activate", () => {
Expand Down