mirror of
https://github.com/napi-rs/napi-rs.git
synced 2025-12-08 19:56:07 +00:00
And recreate it if it does not exist.
In windows, electron renderer process will crash if:
1. Import some NAPI module that enable `tokio-rt` flag in renderer process.
2. Reload the page.
3. Call a function imported from that NAPI module.
Because the tokio runtime will be dropped when reloading the page, and won't create again, but currently we assume that the runtime must exist in tokio-based `within_runtime_if_available`.
This will cause some panic like this:
```
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', napi-rs\crates\napi\src\tokio_runtime.rs:72:42
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Error: Renderer process crashed: crashed, exitCode: -529697949
at EventEmitter.<anonymous> (napi-rs\examples\napi\electron.js:33:9)
at EventEmitter.emit (node:events:525:35)
```
96 lines
2.1 KiB
JavaScript
96 lines
2.1 KiB
JavaScript
const assert = require('assert')
|
|
const { readFileSync } = require('fs')
|
|
|
|
const { app, BrowserWindow, ipcMain } = require('electron')
|
|
|
|
const {
|
|
readFileAsync,
|
|
callThreadsafeFunction,
|
|
withAbortController,
|
|
createExternalTypedArray,
|
|
} = require('./index')
|
|
|
|
const FILE_CONTENT = readFileSync(__filename, 'utf8')
|
|
|
|
const createWindowAndReload = async () => {
|
|
await app.whenReady()
|
|
|
|
const win = new BrowserWindow({
|
|
width: 800,
|
|
height: 600,
|
|
show: false,
|
|
webPreferences: {
|
|
nodeIntegration: true,
|
|
contextIsolation: false,
|
|
},
|
|
})
|
|
|
|
await win.loadFile('./electron-renderer/index.html')
|
|
|
|
await new Promise((resolve, reject) => {
|
|
win.webContents.on('render-process-gone', (e, detail) => {
|
|
reject(
|
|
new Error(
|
|
`Renderer process crashed: ${detail.reason}, exitCode: ${detail.exitCode}`,
|
|
),
|
|
)
|
|
})
|
|
|
|
// reload to check if there is any crash
|
|
win.reload()
|
|
|
|
// Wait for a while to make sure if a crash happens, the 'resolve' function should be called after the crash
|
|
setTimeout(() => {
|
|
// make sure the renderer process is still alive
|
|
ipcMain.once('pong', () => resolve())
|
|
win.webContents.send('ping')
|
|
}, 500)
|
|
})
|
|
}
|
|
|
|
async function main() {
|
|
const ctrl = new AbortController()
|
|
const promise = withAbortController(1, 2, ctrl.signal)
|
|
try {
|
|
ctrl.abort()
|
|
await promise
|
|
throw new Error('Should throw AbortError')
|
|
} catch (err) {
|
|
assert(err.message === 'AbortError')
|
|
}
|
|
|
|
const buf = await readFileAsync(__filename)
|
|
assert(FILE_CONTENT === buf.toString('utf8'))
|
|
|
|
const value = await new Promise((resolve, reject) => {
|
|
let i = 0
|
|
let value = 0
|
|
callThreadsafeFunction((err, v) => {
|
|
if (err != null) {
|
|
reject(err)
|
|
return
|
|
}
|
|
i++
|
|
value += v
|
|
if (i === 100) {
|
|
resolve(value)
|
|
}
|
|
})
|
|
})
|
|
|
|
assert(
|
|
value ===
|
|
Array.from({ length: 100 }, (_, i) => i + 1).reduce((a, b) => a + b),
|
|
)
|
|
console.info(createExternalTypedArray())
|
|
}
|
|
|
|
Promise.all([main(), createWindowAndReload()])
|
|
.then(() => {
|
|
process.exit(0)
|
|
})
|
|
.catch((e) => {
|
|
console.error(e)
|
|
process.exit(1)
|
|
})
|