Skip to content

Commit 41528c3

Browse files
committed
feat(load-emscripten-module-node): support pthread modules
1 parent 1a0673b commit 41528c3

File tree

3 files changed

+80
-8
lines changed

3 files changed

+80
-8
lines changed

packages/core/typescript/itk-wasm/src/pipeline/internal/load-emscripten-module-node.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fs from 'fs'
22
import EmscriptenModule from '../itk-wasm-emscripten-module.js'
33
import { pathToFileURL } from 'url'
44
import { ZSTDDecoder } from '@thewtex/zstddec'
5+
import pthreadSupportAvailable from '../pthread-support-available.js'
56

67
const zstdDecoder = new ZSTDDecoder()
78
await zstdDecoder.init()
@@ -19,9 +20,49 @@ async function loadEmscriptenModuleNode (
1920
if (modulePath.endsWith('.wasm.zst')) {
2021
modulePrefix = modulePath.substring(0, modulePath.length - 9)
2122
}
22-
const compressedWasmBinaryPath = `${modulePrefix}.wasm.zst`
23-
const compressedWasmBinary = fs.readFileSync(compressedWasmBinaryPath)
24-
const wasmBinary = zstdDecoder.decode(new Uint8Array(compressedWasmBinary))
23+
24+
// Check for pthread support and use the appropriate WASM file
25+
const hasPthreadSupport = pthreadSupportAvailable()
26+
let wasmFileName = `${modulePrefix}.wasm.zst`
27+
let wasmBinary: Uint8Array
28+
29+
if (hasPthreadSupport) {
30+
const threadsWasmPath = `${modulePrefix}.threads.wasm.zst`
31+
if (fs.existsSync(threadsWasmPath)) {
32+
wasmFileName = threadsWasmPath
33+
const compressedWasmBinary = fs.readFileSync(wasmFileName)
34+
wasmBinary = zstdDecoder.decode(new Uint8Array(compressedWasmBinary))
35+
} else {
36+
// Fall back to checking for compressed non-threaded version
37+
if (fs.existsSync(wasmFileName)) {
38+
const compressedWasmBinary = fs.readFileSync(wasmFileName)
39+
wasmBinary = zstdDecoder.decode(new Uint8Array(compressedWasmBinary))
40+
} else {
41+
// Fall back to uncompressed WASM file
42+
const uncompressedWasmPath = `${modulePrefix}.wasm`
43+
if (fs.existsSync(uncompressedWasmPath)) {
44+
wasmBinary = fs.readFileSync(uncompressedWasmPath)
45+
} else {
46+
throw new Error(`No WASM file found for module: ${modulePrefix}`)
47+
}
48+
}
49+
}
50+
} else {
51+
// Check for compressed non-threaded version first
52+
if (fs.existsSync(wasmFileName)) {
53+
const compressedWasmBinary = fs.readFileSync(wasmFileName)
54+
wasmBinary = zstdDecoder.decode(new Uint8Array(compressedWasmBinary))
55+
} else {
56+
// Fall back to uncompressed WASM file
57+
const uncompressedWasmPath = `${modulePrefix}.wasm`
58+
if (fs.existsSync(uncompressedWasmPath)) {
59+
wasmBinary = fs.readFileSync(uncompressedWasmPath)
60+
} else {
61+
throw new Error(`No WASM file found for module: ${modulePrefix}`)
62+
}
63+
}
64+
}
65+
2566
const fullModulePath = pathToFileURL(`${modulePrefix}.js`).href
2667
const result = await import(
2768
/* webpackIgnore: true */ /* @vite-ignore */ fullModulePath

packages/core/typescript/itk-wasm/test/node/pipeline/run-pipeline-node-test.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ test('runPipelineNode writes and reads an itk.TransformList via memory io', asyn
477477
verifyTransform(outputs[0].data)
478478
})
479479

480-
test.only('runPipelineNode writes and reads a CompositeTransform itk.TransformList via memory io', async (t) => {
480+
test('runPipelineNode writes and reads a CompositeTransform itk.TransformList via memory io', async (t) => {
481481
const verifyTransform = (transformList) => {
482482
t.is(
483483
transformList.length,
@@ -643,6 +643,39 @@ test.only('runPipelineNode writes and reads a CompositeTransform itk.TransformLi
643643
verifyTransform(outputs[0].data)
644644
})
645645

646+
test('runPipelineNode runs pthreads-enabled pipeline', async (t) => {
647+
const pipelinePath = path.resolve(
648+
'test',
649+
'pipelines',
650+
'emscripten-threads-build',
651+
'pthreads-pipeline',
652+
'pthreads-test'
653+
)
654+
const args = ['--memory-io', '--number-of-threads', '4', '0']
655+
const desiredOutputs = [{ type: InterfaceTypes.JsonCompatible }]
656+
const inputs = []
657+
658+
const { outputs } = await runPipelineNode(
659+
pipelinePath,
660+
args,
661+
desiredOutputs,
662+
inputs
663+
)
664+
665+
const result = outputs[0].data
666+
t.is(typeof result, 'object', 'output should be an object')
667+
t.is(
668+
typeof result.createdThreads,
669+
'number',
670+
'createdThreads should be a number'
671+
)
672+
t.true(result.createdThreads >= 0, 'should have created at least 0 threads')
673+
t.true(
674+
result.createdThreads <= 4,
675+
'should not have created more than 4 threads'
676+
)
677+
})
678+
646679
test('runPipelineNode creates a composite transform with expected parameters', async (t) => {
647680
const verifyCompositeTransform = (transformList) => {
648681
t.is(

packages/core/typescript/itk-wasm/test/pipelines/CMakeLists.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,5 @@ add_subdirectory("mesh-read-write-pipeline")
1010
add_subdirectory("transform-read-write-pipeline")
1111
add_subdirectory("read-image")
1212
add_subdirectory("stdout-stderr-pipeline")
13-
if (NOT EMSCRIPTEN)
14-
add_subdirectory("pthreads-pipeline")
15-
add_subdirectory("cxx-threads-pipeline")
16-
endif()
13+
add_subdirectory("pthreads-pipeline")
14+
add_subdirectory("cxx-threads-pipeline")

0 commit comments

Comments
 (0)