blob: afd10f61b239d6a4684cbebafe17dc5517695c0e (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
// @flow
import { app, ipcMain, ipcRenderer } from 'electron';
import { log } from './lib/platform';
import type { WebContents } from 'electron';
// The timeout before the shutdown is enforced
const SHUTDOWN_TIMEOUT = 3000;
/**
* Shutdown coordinator postpones the application shutdown until ShutdownHandler finished the
* shutdown sequence.
*
* ShutdownCoordinator can only be created from main process.
*/
class ShutdownCoordinator {
_canQuit = false;
_isQuitting = false;
_webContents: WebContents;
_shutdownTimeout: ?TimeoutID;
constructor(webContents: WebContents) {
this._webContents = webContents;
app.on('before-quit', this._onBeforeQuit);
ipcMain.on('app-shutdown-reply', this._onShutdownReply);
}
dispose() {
app
.removeListener('before-quit', this._onBeforeQuit)
.removeListener('app-shutdown-reply', this._onShutdownReply);
}
_onBeforeQuit = (event) => {
if (!this._canQuit) {
// make sure we don't call the shutdown handler twice
if (!this._isQuitting) {
this._isQuitting = true;
// start timer to force shutdown if the renderer process is not able to handle shutdown in
// a timely manner.
this._shutdownTimeout = setTimeout(this._onShutdownTimeout, SHUTDOWN_TIMEOUT);
this._webContents.send('app-shutdown');
}
event.preventDefault();
}
};
_onShutdownReply = () => {
const shutdownTimeout = this._shutdownTimeout;
if (shutdownTimeout) {
clearTimeout(shutdownTimeout);
}
this._grantShutdown();
};
_onShutdownTimeout = () => {
log.warn('It took longer than expected to shutdown. Forcing shutdown now.');
this._grantShutdown();
};
_grantShutdown() {
this._canQuit = true;
app.quit();
}
}
/**
* Executes the provided closure when application is about to quit.
* It works in tandem with ShutdownCoordinator and can only be used in renderer process.
* The shutdown will be postponed until the Promise returned from closure is resolved.
* ShutdownCoordinator will force the shutdown if the returned Promise is not resolved within
* the allocated time (see SHUTDOWN_TIMEOUT).
*/
function setShutdownHandler(handler: () => Promise<void>) {
ipcRenderer.once('app-shutdown', async (event) => {
try {
await handler();
} catch (error) {
log.warn(`An error occurred in shutdown handler: ${error.message}`);
}
event.sender.send('app-shutdown-reply');
});
}
if (ipcMain) {
module.exports = { ShutdownCoordinator };
} else {
module.exports = { setShutdownHandler };
}
|