Fix crash on shutdown

This commit is contained in:
DH
2025-08-25 19:45:42 +03:00
parent 67f52964cd
commit a1bb3a033e
9 changed files with 60 additions and 38 deletions

View File

@@ -91,7 +91,7 @@ tryFetchGame(const std::filesystem::directory_entry &entry) {
auto data = sfo::load(paramSfoPath.string());
if (data.errc != sfo::error::ok) {
elog("%s: error %d", entry.path().c_str(), data.errc);
elog("%s: error %d", entry.path().c_str(), static_cast<int>(data.errc));
return {};
}

View File

@@ -10,7 +10,7 @@ const mainWindow: Window = {
};
export function initialize() {
explorer.pushExplorerView(mainWindow, {
return explorer.pushExplorerView(mainWindow, {
filter: {
type: 'game'
}

View File

@@ -117,8 +117,7 @@ async function activateMainWindow() {
setupElectron();
export function initialize() {
console.log('web initialization');
export async function initialize() {
ipcMain.on('window/create', (_event, options) => {
const win = new BrowserWindow({
webPreferences: {
@@ -130,11 +129,6 @@ export function initialize() {
win.loadURL(`app://-/${options.url}`);
});
// console.log(await github.githubReleases({
// owner: "RPCSX",
// repository: "rpcsx"
// }));
const createWindow = async () => {
await activateMainWindow();
@@ -152,28 +146,31 @@ export function initialize() {
uiInitializedFuture.dispose();
console.log('initialization complete');
explorer.pushExplorerView(toWindow(MainWindow), {
return explorer.pushExplorerView(toWindow(MainWindow), {
filter: {
type: 'game'
}
});
};
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
app.on('window-all-closed', async () => {
await core.shutdown(undefined);
try {
await core.shutdown(undefined);
} catch (e) {
console.error("shutdown throws exception", e);
}
if (process.platform !== 'darwin') {
app.quit();
}
});
await app.whenReady();
return createWindow();
}

View File

@@ -19,6 +19,6 @@ export type ComponentId = string;
export type Component = {
getId(): ComponentId;
onClose(listener: () => void): IDisposable;
onClose(listener: () => void | Promise<void>): IDisposable;
sendEvent(event: string, params?: any): void;
};

View File

@@ -12,8 +12,16 @@ export function onComponentActivation(component: ComponentInstance) {
const rendererComponent: Component = {
getId: () => ":renderer",
onClose: (listener) => {
webContents.on("destroyed", listener);
return Disposable.Create(() => { webContents.off("destroyed", listener); });
const wrapped = async () => {
try {
await listener();
} catch (e) {
console.error("onClose listener throws exception", e);
}
};
webContents.on("destroyed", wrapped);
return Disposable.Create(() => { webContents.off("destroyed", wrapped); });
},
sendEvent: (event, params) => {
webContents.send(`${component.getName()}/${event}`, params);

View File

@@ -207,10 +207,7 @@ export class ComponentInstance implements ComponentContext {
const externalDisposable = externalEmitter.event(listener);
const emitterDisposable = emitter.event(listener);
const disposable = Disposable.Create(() => {
externalDisposable.dispose();
emitterDisposable.dispose();
const disposable = Disposable.Create(async () => {
if (!externalEmitter.hasListeners()) {
delete caller.externalEventEmitter[externalEvent];
}
@@ -219,6 +216,17 @@ export class ComponentInstance implements ComponentContext {
delete this.eventEmitter[event];
}
try {
await externalDisposable.dispose();
} catch (e) {
console.error('externalDisposable throws error', e);
}
try {
await emitterDisposable.dispose();
} catch (e) {
console.error('emitterDisposable throws error', e);
}
});
caller.manage(disposable);

View File

@@ -20,6 +20,10 @@ export async function activate() {
await Promise.all(Object.values(components).map(component => initializeComponent(component.getManifest())));
for (const component of Object.values(components)) {
if (component.getName() == "core") {
continue;
}
try {
await instance.activateComponent(component.getManifest());
} catch (e) {
@@ -34,6 +38,9 @@ export async function deactivate() {
const components = getComponentList();
for (const component of Object.values(components)) {
if (component.getName() == "core") {
continue;
}
try {
await instance.unregisterComponent(component.getId());
} catch (e) {
@@ -189,5 +196,5 @@ export async function handleSettingsGet(caller: Component, request: SettingsGetR
export async function shutdown(caller: Component, _request: ShutdownRequest): Promise<ShutdownResponse> {
console.warn(`shutdown invoked by ${caller.getId()}`);
instance.uninitializeComponent(self.thisComponent().getManifest());
await instance.uninitializeComponent(self.thisComponent().getManifest());
}

View File

@@ -139,22 +139,24 @@ export class ExplorerComponent implements IDisposable {
title: "Explorer progress"
})).channel;
const closeDisposable = caller.onClose(async () => {
delete this.subscriptions[progressChannel];
await progress.progressUpdate({
channel: progressChannel,
status: ProgressStatus.Canceled
});
});
progress.onProgressUpdate(({ value }) => {
if (value.status == ProgressStatus.Canceled) {
closeDisposable.dispose();
delete this.subscriptions[progressChannel];
}
});
this.subscriptions[progressChannel] = caller;
caller.onClose(() => {
progress.progressUpdate({
channel: progressChannel,
status: ProgressStatus.Canceled
});
delete this.subscriptions[progressChannel];
});
self.sendExplorerItemsEvent(caller, {
channel: progressChannel,

View File

@@ -60,14 +60,14 @@ export function progressCreate(source: Component, params: ProgressCreateRequest)
return { channel };
}
export function progressUpdate(caller: Component, params: ProgressUpdateRequest) {
export async function progressUpdate(caller: Component, params: ProgressUpdateRequest) {
const info = channels[params.channel];
if (!info) {
throw { code: ErrorCode.InvalidParams };
}
if (info.creator != caller) {
if (info.creator.getId() != caller.getId()) {
throw { code: ErrorCode.InvalidRequest };
}
@@ -101,7 +101,7 @@ export function progressUpdate(caller: Component, params: ProgressUpdateRequest)
params.status == ProgressStatus.Complete ||
params.status == ProgressStatus.Error) {
if (info.disposable) {
info.disposable.dispose();
await info.disposable.dispose();
}
delete channels[params.channel];