fix(oxide): retain Windows addon after worker exit
This commit is contained in:
parent
f723e834ad
commit
64a8dcd660
2 changed files with 62 additions and 8 deletions
|
|
@ -5,6 +5,38 @@ extern crate napi_derive;
|
|||
|
||||
mod utf16;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[napi(module_exports)]
|
||||
fn retain_native_library() -> napi::Result<()> {
|
||||
use std::ffi::c_void;
|
||||
|
||||
#[link(name = "kernel32")]
|
||||
extern "system" {
|
||||
fn GetModuleHandleExW(flags: u32, address: *const u16, module: *mut *mut c_void) -> i32;
|
||||
}
|
||||
|
||||
const FROM_ADDRESS: u32 = 0x00000004;
|
||||
const PIN: u32 = 0x00000001;
|
||||
let mut module = std::ptr::null_mut();
|
||||
// Rayon threads may outlive a Node worker. Keep their code mapped until process exit.
|
||||
// SAFETY: FROM_ADDRESS treats this function's address as a module location, not a string.
|
||||
// The module is executing this registration hook and `module` is a valid output pointer.
|
||||
let retained = unsafe {
|
||||
GetModuleHandleExW(
|
||||
FROM_ADDRESS | PIN,
|
||||
retain_native_library as *const () as *const u16,
|
||||
&mut module,
|
||||
)
|
||||
};
|
||||
if retained == 0 {
|
||||
return Err(napi::Error::from_reason(format!(
|
||||
"Failed to retain the Oxide native library: {}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[napi(object)]
|
||||
pub struct ChangedContent {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { js, json, test } from '../utils'
|
||||
|
||||
test(
|
||||
'@tailwindcss/oxide can be loaded into a Node.js worker thread',
|
||||
'@tailwindcss/oxide can scan in a Node.js worker and survive worker exit',
|
||||
{
|
||||
fs: {
|
||||
'package.json': json`
|
||||
|
|
@ -12,21 +12,43 @@ test(
|
|||
}
|
||||
`,
|
||||
'start.js': js`
|
||||
let assert = require('node:assert/strict')
|
||||
let { Worker } = require('worker_threads')
|
||||
new Worker('./worker.js')
|
||||
let worker = new Worker('./worker.js')
|
||||
worker.on('error', (error) => {
|
||||
throw error
|
||||
})
|
||||
worker.on('exit', (code) => {
|
||||
assert.equal(code, 0)
|
||||
if (process.platform === 'win32') {
|
||||
assert.ok(
|
||||
process.report
|
||||
.getReport()
|
||||
.sharedObjects.some((file) => /tailwindcss-oxide.*[.]node$/.test(file)),
|
||||
'Oxide must remain mapped after the worker exits',
|
||||
)
|
||||
}
|
||||
setTimeout(() => console.log('parent survived worker exit'), 500)
|
||||
})
|
||||
`,
|
||||
'worker.js': js`
|
||||
require('@tailwindcss/oxide')
|
||||
let assert = require('node:assert/strict')
|
||||
let { Scanner } = require('@tailwindcss/oxide')
|
||||
let scanner = new Scanner({ sources: [] })
|
||||
let candidates = scanner.scanFiles(
|
||||
Array.from({ length: 100 }, () => ({
|
||||
content: '<div class="flex p-4 text-red-500"></div>',
|
||||
extension: 'html',
|
||||
})),
|
||||
)
|
||||
assert.ok(candidates.includes('flex'))
|
||||
process.on('exit', () => console.log('worker thread exited'))
|
||||
`,
|
||||
},
|
||||
},
|
||||
async ({ exec, expect }) => {
|
||||
let output = await exec('node ./start.js').then(
|
||||
(out) => out.trim(),
|
||||
(err) => `${err}`,
|
||||
)
|
||||
let output = await exec('node ./start.js').then((out) => out.trim())
|
||||
|
||||
expect(output).toEqual('worker thread exited')
|
||||
expect(output).toEqual('worker thread exited\nparent survived worker exit')
|
||||
},
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue