emnapi 0.32.2 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +3 -3
- package/README.md +211 -99
- package/dist/library_napi.js +41 -24
- package/include/emnapi.h +1 -1
- package/include/js_native_api.h +1 -1
- package/package.json +1 -1
- package/src/async_cleanup_hook.c +1 -1
- package/src/async_context.c +1 -1
- package/src/async_work.c +1 -1
- package/src/emnapi.c +1 -1
- package/src/js_native_api.c +1 -1
- package/src/node_api.c +1 -1
- package/src/threadsafe_function.c +1 -1
- package/src/uv/threadpool.c +36 -5
- package/src/uv/unix/async.c +1 -1
- package/src/uv/unix/thread.c +10 -1
- /package/include/{common.h → emnapi_common.h} +0 -0
- /package/src/{emnapi_common.h → emnapi_internal.h} +0 -0
package/CMakeLists.txt
CHANGED
|
@@ -44,7 +44,7 @@ set(EMNAPI_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
|
|
44
44
|
set(EMNAPI_JS_LIB "${CMAKE_CURRENT_SOURCE_DIR}/dist/library_napi.js")
|
|
45
45
|
|
|
46
46
|
if(IS_EMSCRIPTEN)
|
|
47
|
-
set(EMNAPI_MT_CFLAGS "-
|
|
47
|
+
set(EMNAPI_MT_CFLAGS "-pthread" "-sWASM_WORKERS=1")
|
|
48
48
|
else()
|
|
49
49
|
set(EMNAPI_MT_CFLAGS "-pthread")
|
|
50
50
|
endif()
|
|
@@ -148,7 +148,7 @@ if(LIB_ARCH)
|
|
|
148
148
|
endif()
|
|
149
149
|
|
|
150
150
|
install(FILES
|
|
151
|
-
${CMAKE_CURRENT_SOURCE_DIR}/include/
|
|
151
|
+
${CMAKE_CURRENT_SOURCE_DIR}/include/emnapi_common.h
|
|
152
152
|
${CMAKE_CURRENT_SOURCE_DIR}/include/emnapi.h
|
|
153
153
|
${CMAKE_CURRENT_SOURCE_DIR}/include/js_native_api_types.h
|
|
154
154
|
${CMAKE_CURRENT_SOURCE_DIR}/include/js_native_api.h
|
|
@@ -170,7 +170,7 @@ install(FILES
|
|
|
170
170
|
if(EMNAPI_INSTALL_SRC)
|
|
171
171
|
install(FILES
|
|
172
172
|
${EMNAPI_SRC}
|
|
173
|
-
"${CMAKE_CURRENT_SOURCE_DIR}/src/
|
|
173
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/src/emnapi_internal.h"
|
|
174
174
|
DESTINATION "src/${PROJECT_NAME}")
|
|
175
175
|
install(DIRECTORY
|
|
176
176
|
${CMAKE_CURRENT_SOURCE_DIR}/src/uv
|
package/README.md
CHANGED
|
@@ -6,9 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://github.com/toyobayashi/emnapi/actions/workflows/main.yml)
|
|
8
8
|
|
|
9
|
-
[Node-API](https://nodejs.org/docs/latest/api/n-api.html) implementation for [Emscripten](https://emscripten.org/index.html), [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) and clang
|
|
10
|
-
|
|
11
|
-
Emscripten is the first class support target, currently thread related APIs are unavailable on `wasm32-unknown-unknown` and `wasm32-wasi` target.
|
|
9
|
+
[Node-API](https://nodejs.org/docs/latest/api/n-api.html) implementation for [Emscripten](https://emscripten.org/index.html), [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) and clang with wasm support. [napi-rs support is comming soon](https://github.com/napi-rs/napi-rs/tree/emnapi).
|
|
12
10
|
|
|
13
11
|
This project aims to
|
|
14
12
|
|
|
@@ -27,6 +25,16 @@ See documentation for more details:
|
|
|
27
25
|
|
|
28
26
|
[How to build Node-API official examples](https://github.com/toyobayashi/node-addon-examples)
|
|
29
27
|
|
|
28
|
+
Emscripten is the first class support target. If your target is running addon on browser,
|
|
29
|
+
we strongly recommend you to use Emscripten instead of wasi-sdk. Async works and threadsafe
|
|
30
|
+
functions related APIs are only available on Emscripten or `wasm32-wasi-threads` target since
|
|
31
|
+
they are relying on pthread. Though today we have [WASI browser polyfill](https://github.com/toyobayashi/wasm-util),
|
|
32
|
+
`wasm32-wasi-threads` is in very early stage and WASI itself is not designed for browser.
|
|
33
|
+
There are some limitations on browser about wasi-libc's pthread implementation, for example
|
|
34
|
+
`pthread_mutex_lock` may call `__builtin_wasm_memory_atomic_wait32`(`memory.atomic.wait32`)
|
|
35
|
+
which is disallowed in browser JS main thread. While Emscripten's pthread implementation
|
|
36
|
+
has considered usage in browser.
|
|
37
|
+
|
|
30
38
|
## Prerequests
|
|
31
39
|
|
|
32
40
|
You will need to install:
|
|
@@ -332,30 +340,19 @@ For non-emscripten, you need to use `@emnapi/core`. The initialization is simila
|
|
|
332
340
|
<script src="node_modules/@emnapi/runtime/dist/emnapi.min.js"></script>
|
|
333
341
|
<script src="node_modules/@emnapi/core/dist/emnapi-core.min.js"></script>
|
|
334
342
|
<script>
|
|
335
|
-
|
|
336
|
-
context: emnapi.getDefaultContext()
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
napi: napiModule.imports.napi,
|
|
349
|
-
emnapi: napiModule.imports.emnapi
|
|
350
|
-
})
|
|
351
|
-
}).then(({ instance, module }) => {
|
|
352
|
-
const binding = napiModule.init({
|
|
353
|
-
instance, // WebAssembly.Instance
|
|
354
|
-
module, // WebAssembly.Module
|
|
355
|
-
memory: instance.exports.memory, // WebAssembly.Memory
|
|
356
|
-
table: instance.exports.__indirect_function_table // WebAssembly.Table
|
|
357
|
-
})
|
|
358
|
-
// binding === napiModule.exports
|
|
343
|
+
emnapiCore.instantiateNapiModule(fetch('./hello.wasm'), {
|
|
344
|
+
context: emnapi.getDefaultContext(),
|
|
345
|
+
overwriteImports (importObject) {
|
|
346
|
+
// importObject.env = {
|
|
347
|
+
// ...importObject.env,
|
|
348
|
+
// ...importObject.napi,
|
|
349
|
+
// ...importObject.emnapi,
|
|
350
|
+
// // ...
|
|
351
|
+
// }
|
|
352
|
+
}
|
|
353
|
+
}).then(({ instance, module, napiModule }) => {
|
|
354
|
+
const binding = napiModule.exports
|
|
355
|
+
// ...
|
|
359
356
|
})
|
|
360
357
|
</script>
|
|
361
358
|
```
|
|
@@ -363,36 +360,25 @@ fetch('./hello.wasm').then(res => res.arrayBuffer()).then(wasmBuffer => {
|
|
|
363
360
|
Using WASI on Node.js
|
|
364
361
|
|
|
365
362
|
```js
|
|
366
|
-
const {
|
|
363
|
+
const { instantiateNapiModule } = require('@emnapi/core')
|
|
367
364
|
const { getDefaultContext } = require('@emnapi/runtime')
|
|
368
365
|
const { WASI } = require('wasi')
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
})
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
...
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
//
|
|
385
|
-
napi: napiModule.imports.napi,
|
|
386
|
-
emnapi: napiModule.imports.emnapi
|
|
387
|
-
}).then(({ instance, module }) => {
|
|
388
|
-
wasi.initialize(instance)
|
|
389
|
-
const binding = napiModule.init({
|
|
390
|
-
instance,
|
|
391
|
-
module,
|
|
392
|
-
memory: instance.exports.memory,
|
|
393
|
-
table: instance.exports.__indirect_function_table
|
|
394
|
-
})
|
|
395
|
-
// binding === napiModule.exports
|
|
366
|
+
const fs = require('fs')
|
|
367
|
+
|
|
368
|
+
instantiateNapiModule(fs.promises.readFile('./hello.wasm'), {
|
|
369
|
+
wasi: new WASI({ /* ... */ }),
|
|
370
|
+
context: getDefaultContext(),
|
|
371
|
+
overwriteImports (importObject) {
|
|
372
|
+
// importObject.env = {
|
|
373
|
+
// ...importObject.env,
|
|
374
|
+
// ...importObject.napi,
|
|
375
|
+
// ...importObject.emnapi,
|
|
376
|
+
// // ...
|
|
377
|
+
// }
|
|
378
|
+
}
|
|
379
|
+
}).then(({ instance, module, napiModule }) => {
|
|
380
|
+
const binding = napiModule.exports
|
|
381
|
+
// ...
|
|
396
382
|
})
|
|
397
383
|
```
|
|
398
384
|
|
|
@@ -400,38 +386,26 @@ Using WASI on browser, you can use WASI polyfill in [wasm-util](https://github.c
|
|
|
400
386
|
and [memfs-browser](https://github.com/toyobayashi/memfs-browser)
|
|
401
387
|
|
|
402
388
|
```js
|
|
403
|
-
import {
|
|
389
|
+
import { instantiateNapiModule } from '@emnapi/core'
|
|
404
390
|
import { getDefaultContext } from '@emnapi/runtime'
|
|
405
391
|
import { WASI } from '@tybys/wasm-util'
|
|
406
|
-
import {
|
|
407
|
-
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
})
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
// clang
|
|
424
|
-
napi: napiModule.imports.napi,
|
|
425
|
-
emnapi: napiModule.imports.emnapi
|
|
426
|
-
}).then(({ instance, module }) => {
|
|
427
|
-
wasi.initialize(instance)
|
|
428
|
-
const binding = napiModule.init({
|
|
429
|
-
instance,
|
|
430
|
-
module,
|
|
431
|
-
memory: instance.exports.memory,
|
|
432
|
-
table: instance.exports.__indirect_function_table
|
|
433
|
-
})
|
|
434
|
-
// binding === napiModule.exports
|
|
392
|
+
import { Volume, createFsFromVolume } from 'memfs-browser'
|
|
393
|
+
|
|
394
|
+
const fs = createFsFromVolume(Volume.fromJSON({ /* ... */ }))
|
|
395
|
+
return instantiateNapiModule(fetch('./hello.wasm'), {
|
|
396
|
+
wasi: new WASI({ fs, /* ... */ })
|
|
397
|
+
context: getDefaultContext(),
|
|
398
|
+
overwriteImports (importObject) {
|
|
399
|
+
// importObject.env = {
|
|
400
|
+
// ...importObject.env,
|
|
401
|
+
// ...importObject.napi,
|
|
402
|
+
// ...importObject.emnapi,
|
|
403
|
+
// // ...
|
|
404
|
+
// }
|
|
405
|
+
}
|
|
406
|
+
}).then(({ instance, module, napiModule }) => {
|
|
407
|
+
const binding = napiModule.exports
|
|
408
|
+
// ...
|
|
435
409
|
})
|
|
436
410
|
```
|
|
437
411
|
|
|
@@ -495,6 +469,7 @@ clang++ -O3 \
|
|
|
495
469
|
-L./node_modules/emnapi/lib/wasm32-wasi \
|
|
496
470
|
--target=wasm32-wasi \
|
|
497
471
|
--sysroot=$WASI_SDK_PATH/share/wasi-sysroot \
|
|
472
|
+
-fno-exceptions \
|
|
498
473
|
-mexec-model=reactor \
|
|
499
474
|
-Wl,--initial-memory=16777216 \
|
|
500
475
|
-Wl,--export-dynamic \
|
|
@@ -522,6 +497,7 @@ clang++ -O3 \
|
|
|
522
497
|
-I./node_modules/emnapi/include \
|
|
523
498
|
-L./node_modules/emnapi/lib/wasm32 \
|
|
524
499
|
--target=wasm32 \
|
|
500
|
+
-fno-exceptions \
|
|
525
501
|
-nostdlib \
|
|
526
502
|
-Wl,--no-entry \
|
|
527
503
|
-Wl,--initial-memory=16777216 \
|
|
@@ -575,12 +551,14 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
|
|
575
551
|
"-sEXPORTED_FUNCTIONS=['_malloc','_free']"
|
|
576
552
|
)
|
|
577
553
|
elseif(CMAKE_SYSTEM_NAME STREQUAL "WASI")
|
|
554
|
+
set_target_properties(hello PROPERTIES SUFFIX ".wasm")
|
|
578
555
|
target_link_options(hello PRIVATE
|
|
579
556
|
"-mexec-model=reactor"
|
|
580
557
|
"-Wl,--export=napi_register_wasm_v1"
|
|
581
558
|
"-Wl,--initial-memory=16777216,--export-dynamic,--export=malloc,--export=free,--import-undefined,--export-table"
|
|
582
559
|
)
|
|
583
560
|
elseif((CMAKE_C_COMPILER_TARGET STREQUAL "wasm32") OR (CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-unknown-unknown"))
|
|
561
|
+
set_target_properties(hello PROPERTIES SUFFIX ".wasm")
|
|
584
562
|
target_link_options(hello PRIVATE
|
|
585
563
|
"-nostdlib"
|
|
586
564
|
"-Wl,--export=napi_register_wasm_v1"
|
|
@@ -752,35 +730,162 @@ pub unsafe extern "C" fn napi_register_wasm_v1(env: napi_env, exports: napi_valu
|
|
|
752
730
|
|
|
753
731
|
</details>
|
|
754
732
|
|
|
755
|
-
### Multithread
|
|
733
|
+
### Multithread
|
|
756
734
|
|
|
757
735
|
If you want to use async work or thread safe functions,
|
|
758
736
|
there are additional C source file need to be compiled and linking.
|
|
759
737
|
Recommend use CMake directly.
|
|
760
738
|
|
|
739
|
+
**This is EXPERIMENTAL on non-emscripten.**
|
|
740
|
+
|
|
761
741
|
```cmake
|
|
762
742
|
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/node_modules/emnapi")
|
|
763
743
|
|
|
764
744
|
add_executable(hello hello.c)
|
|
765
745
|
|
|
766
746
|
target_link_libraries(hello emnapi-mt)
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
"-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
747
|
+
|
|
748
|
+
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
|
749
|
+
target_compile_options(hello PRIVATE "-pthread")
|
|
750
|
+
target_link_options(hello PRIVATE
|
|
751
|
+
"-sALLOW_MEMORY_GROWTH=1"
|
|
752
|
+
"-sEXPORTED_FUNCTIONS=['_malloc','_free']"
|
|
753
|
+
"-pthread"
|
|
754
|
+
"-sPTHREAD_POOL_SIZE=4"
|
|
755
|
+
# try to specify stack size if you experience pthread errors
|
|
756
|
+
"-sSTACK_SIZE=2MB"
|
|
757
|
+
"-sDEFAULT_PTHREAD_STACK_SIZE=2MB"
|
|
758
|
+
)
|
|
759
|
+
elseif(CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi-threads")
|
|
760
|
+
# Experimental
|
|
761
|
+
set_target_properties(hello PROPERTIES SUFFIX ".wasm")
|
|
762
|
+
target_compile_options(hello PRIVATE "-fno-exceptions" "-pthread")
|
|
763
|
+
target_link_options(hello PRIVATE
|
|
764
|
+
"-pthread"
|
|
765
|
+
"-mexec-model=reactor"
|
|
766
|
+
"-Wl,--import-memory"
|
|
767
|
+
"-Wl,--max-memory=2147483648"
|
|
768
|
+
"-Wl,--export-dynamic"
|
|
769
|
+
"-Wl,--export=malloc"
|
|
770
|
+
"-Wl,--export=free"
|
|
771
|
+
"-Wl,--import-undefined"
|
|
772
|
+
"-Wl,--export-table"
|
|
773
|
+
)
|
|
774
|
+
endif()
|
|
777
775
|
```
|
|
778
776
|
|
|
779
777
|
```bash
|
|
778
|
+
# emscripten
|
|
780
779
|
emcmake cmake -DCMAKE_BUILD_TYPE=Release -DEMNAPI_WORKER_POOL_SIZE=4 -G Ninja -H. -Bbuild
|
|
780
|
+
|
|
781
|
+
# wasi-sdk with thread support (Experimental)
|
|
782
|
+
cmake -DCMAKE_TOOLCHAIN_FILE=$WASI_SDK_PATH/share/cmake/wasi-sdk-pthread.cmake \
|
|
783
|
+
-DWASI_SDK_PREFIX=$WASI_SDK_PATH \
|
|
784
|
+
-DCMAKE_BUILD_TYPE=Release \
|
|
785
|
+
-G Ninja -H. -Bbuild
|
|
786
|
+
|
|
781
787
|
cmake --build build
|
|
782
788
|
```
|
|
783
789
|
|
|
790
|
+
And additional work is required during instantiating wasm compiled with non-emscripten.
|
|
791
|
+
|
|
792
|
+
```js
|
|
793
|
+
// emnapi main thread (could be in a Worker)
|
|
794
|
+
instantiateNapiModule(input, {
|
|
795
|
+
context: getDefaultContext(),
|
|
796
|
+
wasi: new WASI(/* ... */),
|
|
797
|
+
// reuseWorker: true,
|
|
798
|
+
onCreateWorker () {
|
|
799
|
+
return new Worker('./worker.js')
|
|
800
|
+
// Node.js
|
|
801
|
+
// const { Worker } = require('worker_threads')
|
|
802
|
+
// return new Worker(join(__dirname, './worker.js'), {
|
|
803
|
+
// env: process.env,
|
|
804
|
+
// execArgv: ['--experimental-wasi-unstable-preview1']
|
|
805
|
+
// })
|
|
806
|
+
},
|
|
807
|
+
overwriteImports (importObject) {
|
|
808
|
+
importObject.env.memory = new WebAssembly.Memory({
|
|
809
|
+
initial: 16777216 / 65536,
|
|
810
|
+
maximum: 2147483648 / 65536,
|
|
811
|
+
shared: true
|
|
812
|
+
})
|
|
813
|
+
}
|
|
814
|
+
})
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
```js
|
|
818
|
+
// worker.js
|
|
819
|
+
(function () {
|
|
820
|
+
let fs, WASI, emnapiCore
|
|
821
|
+
|
|
822
|
+
const ENVIRONMENT_IS_NODE =
|
|
823
|
+
typeof process === 'object' && process !== null &&
|
|
824
|
+
typeof process.versions === 'object' && process.versions !== null &&
|
|
825
|
+
typeof process.versions.node === 'string'
|
|
826
|
+
|
|
827
|
+
if (ENVIRONMENT_IS_NODE) {
|
|
828
|
+
const nodeWorkerThreads = require('worker_threads')
|
|
829
|
+
|
|
830
|
+
const parentPort = nodeWorkerThreads.parentPort
|
|
831
|
+
|
|
832
|
+
parentPort.on('message', (data) => {
|
|
833
|
+
globalThis.onmessage({ data })
|
|
834
|
+
})
|
|
835
|
+
|
|
836
|
+
fs = require('fs')
|
|
837
|
+
|
|
838
|
+
Object.assign(globalThis, {
|
|
839
|
+
self: globalThis,
|
|
840
|
+
require,
|
|
841
|
+
Worker: nodeWorkerThreads.Worker,
|
|
842
|
+
importScripts: function (f) {
|
|
843
|
+
(0, eval)(fs.readFileSync(f, 'utf8') + '//# sourceURL=' + f)
|
|
844
|
+
},
|
|
845
|
+
postMessage: function (msg) {
|
|
846
|
+
parentPort.postMessage(msg)
|
|
847
|
+
}
|
|
848
|
+
})
|
|
849
|
+
|
|
850
|
+
WASI = require('./wasi').WASI
|
|
851
|
+
emnapiCore = require('@emnapi/core')
|
|
852
|
+
} else {
|
|
853
|
+
importScripts('./node_modules/memfs-browser/dist/memfs.js')
|
|
854
|
+
importScripts('./node_modules/@tybys/wasm-util/dist/wasm-util.min.js')
|
|
855
|
+
importScripts('./node_modules/@emnapi/core/dist/emnapi-core.js')
|
|
856
|
+
emnapiCore = globalThis.emnapiCore
|
|
857
|
+
|
|
858
|
+
const { Volume, createFsFromVolume } = memfs
|
|
859
|
+
fs = createFsFromVolume(Volume.fromJSON({
|
|
860
|
+
'/': null
|
|
861
|
+
}))
|
|
862
|
+
|
|
863
|
+
WASI = globalThis.wasmUtil.WASI
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const { instantiateNapiModuleSync, MessageHandler } = emnapiCore
|
|
867
|
+
|
|
868
|
+
const handler = new MessageHandler({
|
|
869
|
+
onLoad ({ wasmModule, wasmMemory }) {
|
|
870
|
+
const wasi = new WASI({ fs })
|
|
871
|
+
|
|
872
|
+
return instantiateNapiModuleSync(wasmModule, {
|
|
873
|
+
childThread: true,
|
|
874
|
+
wasi,
|
|
875
|
+
overwriteImports (importObject) {
|
|
876
|
+
importObject.env.memory = wasmMemory
|
|
877
|
+
}
|
|
878
|
+
})
|
|
879
|
+
}
|
|
880
|
+
})
|
|
881
|
+
|
|
882
|
+
globalThis.onmessage = function (e) {
|
|
883
|
+
handler.handle(e)
|
|
884
|
+
// handle other messages
|
|
885
|
+
}
|
|
886
|
+
})()
|
|
887
|
+
```
|
|
888
|
+
|
|
784
889
|
## Preprocess Macro Options
|
|
785
890
|
|
|
786
891
|
### `-DEMNAPI_WORKER_POOL_SIZE=4`
|
|
@@ -794,6 +899,13 @@ Module.preRun.push(function () {
|
|
|
794
899
|
ENV.UV_THREADPOOL_SIZE = '2';
|
|
795
900
|
}
|
|
796
901
|
});
|
|
902
|
+
|
|
903
|
+
// wasi
|
|
904
|
+
new WASI({
|
|
905
|
+
env: {
|
|
906
|
+
UV_THREADPOOL_SIZE: '2'
|
|
907
|
+
}
|
|
908
|
+
})
|
|
797
909
|
```
|
|
798
910
|
|
|
799
911
|
It represent max of `EMNAPI_WORKER_POOL_SIZE` async work (`napi_queue_async_work`) can be executed in parallel. Default is not defined, read `UV_THREADPOOL_SIZE` at runtime.
|
|
@@ -802,7 +914,7 @@ You can set both `PTHREAD_POOL_SIZE` and `EMNAPI_WORKER_POOL_SIZE` to `number of
|
|
|
802
914
|
If you use another library function which may create `N` child threads in async work,
|
|
803
915
|
then you need to set `PTHREAD_POOL_SIZE` to `EMNAPI_WORKER_POOL_SIZE * (N + 1)`.
|
|
804
916
|
|
|
805
|
-
This option only has effect if you use `-
|
|
917
|
+
This option only has effect if you use `-pthread`.
|
|
806
918
|
Emnapi will create `EMNAPI_WORKER_POOL_SIZE` threads when initializing,
|
|
807
919
|
it will throw error if `PTHREAD_POOL_SIZE < EMNAPI_WORKER_POOL_SIZE && PTHREAD_POOL_SIZE_STRICT == 2`.
|
|
808
920
|
|
|
@@ -810,7 +922,7 @@ See [Issue #8](https://github.com/toyobayashi/emnapi/issues/8) for more detail.
|
|
|
810
922
|
|
|
811
923
|
### `-DEMNAPI_NEXTTICK_TYPE=0`
|
|
812
924
|
|
|
813
|
-
This option only has effect if you use `-
|
|
925
|
+
This option only has effect if you use `-pthread`, Default is `0`.
|
|
814
926
|
Tell emnapi how to delay async work in `uv_async_send` / `uv__async_close`.
|
|
815
927
|
|
|
816
928
|
- `0`: Use `setImmediate()` (Node.js native `setImmediate` or browser `MessageChannel` and `port.postMessage`)
|
|
@@ -818,7 +930,7 @@ Tell emnapi how to delay async work in `uv_async_send` / `uv__async_close`.
|
|
|
818
930
|
|
|
819
931
|
### `-DEMNAPI_USE_PROXYING=1`
|
|
820
932
|
|
|
821
|
-
This option only has effect if you use `-
|
|
933
|
+
This option only has effect if you use emscripten `-pthread`. Default is `1` if emscripten version `>= 3.1.9`, else `0`.
|
|
822
934
|
|
|
823
935
|
- `0`
|
|
824
936
|
|
package/dist/library_napi.js
CHANGED
|
@@ -1458,7 +1458,9 @@ var emnapiExternalMemory = {
|
|
|
1458
1458
|
}
|
|
1459
1459
|
return view;
|
|
1460
1460
|
}
|
|
1461
|
-
|
|
1461
|
+
var maybeOldWasmMemory = emnapiExternalMemory.isDetachedArrayBuffer(view.buffer) ||
|
|
1462
|
+
((typeof SharedArrayBuffer === 'function') && (view.buffer instanceof SharedArrayBuffer));
|
|
1463
|
+
if (maybeOldWasmMemory && emnapiExternalMemory.wasmMemoryViewTable.has(view)) {
|
|
1462
1464
|
var info = emnapiExternalMemory.wasmMemoryViewTable.get(view);
|
|
1463
1465
|
var Ctor = info.Ctor;
|
|
1464
1466
|
var newView = void 0;
|
|
@@ -4667,29 +4669,41 @@ mergeInto(LibraryManager.library, {
|
|
|
4667
4669
|
'return r;' +
|
|
4668
4670
|
'}; })();',
|
|
4669
4671
|
$emnapiAddSendListener: function (worker) {
|
|
4670
|
-
if (
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
4681
|
-
var callback = data.emnapiAsyncSend.callback;
|
|
4682
|
-
{{{ makeDynCall('vp', 'callback') }}}(data.emnapiAsyncSend.data);
|
|
4683
|
-
}
|
|
4672
|
+
if (!worker)
|
|
4673
|
+
return false;
|
|
4674
|
+
if (worker._emnapiSendListener)
|
|
4675
|
+
return true;
|
|
4676
|
+
var handler = function (e) {
|
|
4677
|
+
var data = ENVIRONMENT_IS_NODE ? e : e.data;
|
|
4678
|
+
var __emnapi__ = data.__emnapi__;
|
|
4679
|
+
if (__emnapi__ && __emnapi__.type === 'async-send') {
|
|
4680
|
+
if (ENVIRONMENT_IS_PTHREAD) {
|
|
4681
|
+
postMessage({ __emnapi__: __emnapi__ });
|
|
4684
4682
|
}
|
|
4685
|
-
|
|
4683
|
+
else {
|
|
4684
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
4685
|
+
var callback = __emnapi__.payload.callback;
|
|
4686
|
+
{{{ makeDynCall('vp', 'callback') }}}(__emnapi__.payload.data);
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4689
|
+
};
|
|
4690
|
+
var dispose = function () {
|
|
4686
4691
|
if (ENVIRONMENT_IS_NODE) {
|
|
4687
|
-
worker.
|
|
4692
|
+
worker.off('message', handler);
|
|
4688
4693
|
}
|
|
4689
4694
|
else {
|
|
4690
|
-
worker.
|
|
4695
|
+
worker.removeEventListener('message', handler, false);
|
|
4691
4696
|
}
|
|
4697
|
+
delete worker._emnapiSendListener;
|
|
4698
|
+
};
|
|
4699
|
+
worker._emnapiSendListener = { handler: handler, dispose: dispose };
|
|
4700
|
+
if (ENVIRONMENT_IS_NODE) {
|
|
4701
|
+
worker.on('message', handler);
|
|
4692
4702
|
}
|
|
4703
|
+
else {
|
|
4704
|
+
worker.addEventListener('message', handler, false);
|
|
4705
|
+
}
|
|
4706
|
+
return true;
|
|
4693
4707
|
},
|
|
4694
4708
|
_emnapi_async_send_js__sig: 'vipp',
|
|
4695
4709
|
_emnapi_async_send_js__deps: [
|
|
@@ -4700,9 +4714,12 @@ mergeInto(LibraryManager.library, {
|
|
|
4700
4714
|
_emnapi_async_send_js: function (type, callback, data) {
|
|
4701
4715
|
if (ENVIRONMENT_IS_PTHREAD) {
|
|
4702
4716
|
postMessage({
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4717
|
+
__emnapi__: {
|
|
4718
|
+
type: 'async-send',
|
|
4719
|
+
payload: {
|
|
4720
|
+
callback: callback,
|
|
4721
|
+
data: data
|
|
4722
|
+
}
|
|
4706
4723
|
}
|
|
4707
4724
|
});
|
|
4708
4725
|
}
|
|
@@ -4968,14 +4985,14 @@ function emnapiImplement(name, sig, compilerTimeFunction, deps) {
|
|
|
4968
4985
|
mergeInto(LibraryManager.library, sym);
|
|
4969
4986
|
}
|
|
4970
4987
|
function emnapiImplement2() {
|
|
4971
|
-
|
|
4988
|
+
emnapiImplement.apply(null, arguments);
|
|
4972
4989
|
}
|
|
4973
4990
|
function emnapiImplementInternal() {
|
|
4974
|
-
|
|
4991
|
+
emnapiImplement.apply(null, arguments);
|
|
4975
4992
|
}
|
|
4976
4993
|
// $emnapi*
|
|
4977
4994
|
function emnapiImplementHelper(name, sig, compilerTimeFunction, deps, _exportName) {
|
|
4978
|
-
|
|
4995
|
+
emnapiImplement(name, sig, compilerTimeFunction, deps);
|
|
4979
4996
|
}
|
|
4980
4997
|
function emnapiDefineVar(name, value, deps, postset) {
|
|
4981
4998
|
var _a;
|
package/include/emnapi.h
CHANGED
package/include/js_native_api.h
CHANGED
package/package.json
CHANGED
package/src/async_cleanup_hook.c
CHANGED
package/src/async_context.c
CHANGED
package/src/async_work.c
CHANGED
package/src/emnapi.c
CHANGED
package/src/js_native_api.c
CHANGED
package/src/node_api.c
CHANGED
package/src/uv/threadpool.c
CHANGED
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
#include <errno.h>
|
|
28
28
|
#include <string.h>
|
|
29
29
|
#include "uv-common.h"
|
|
30
|
-
#include "
|
|
30
|
+
#include "emnapi_common.h"
|
|
31
|
+
|
|
32
|
+
#if defined(__wasi__) && defined(_REENTRANT)
|
|
33
|
+
#define __EMNAPI_WASI_THREADS__
|
|
34
|
+
#endif
|
|
31
35
|
|
|
32
36
|
#define MAX_THREADPOOL_SIZE 1024
|
|
33
37
|
|
|
@@ -54,6 +58,15 @@ static void uv__cancelled(struct uv__work* w) {
|
|
|
54
58
|
|
|
55
59
|
EMNAPI_INTERNAL_EXTERN void _emnapi_worker_unref(uv_thread_t pid);
|
|
56
60
|
|
|
61
|
+
#ifdef __EMNAPI_WASI_THREADS__
|
|
62
|
+
EMNAPI_INTERNAL_EXTERN
|
|
63
|
+
void _emnapi_after_uvthreadpool_ready(void (*callback)(QUEUE* w, enum uv__work_kind kind),
|
|
64
|
+
QUEUE* w,
|
|
65
|
+
enum uv__work_kind kind);
|
|
66
|
+
EMNAPI_INTERNAL_EXTERN void _emnapi_tell_js_uvthreadpool(uv_thread_t* threads, unsigned int n);
|
|
67
|
+
EMNAPI_INTERNAL_EXTERN void _emnapi_emit_async_thread_ready();
|
|
68
|
+
#endif
|
|
69
|
+
|
|
57
70
|
/* To avoid deadlock with uv_cancel() it's crucial that the worker
|
|
58
71
|
* never holds the global mutex and the loop-local mutex at the same time.
|
|
59
72
|
*/
|
|
@@ -61,8 +74,11 @@ static void* worker(void* arg) {
|
|
|
61
74
|
struct uv__work* w;
|
|
62
75
|
QUEUE* q;
|
|
63
76
|
int is_slow_work;
|
|
64
|
-
|
|
77
|
+
#ifndef __EMNAPI_WASI_THREADS__
|
|
65
78
|
uv_sem_post((uv_sem_t*) arg);
|
|
79
|
+
#else
|
|
80
|
+
_emnapi_emit_async_thread_ready();
|
|
81
|
+
#endif
|
|
66
82
|
arg = NULL;
|
|
67
83
|
|
|
68
84
|
uv_mutex_lock(&mutex);
|
|
@@ -199,7 +215,9 @@ static void init_threads(void) {
|
|
|
199
215
|
#if !defined(EMNAPI_WORKER_POOL_SIZE) || !(EMNAPI_WORKER_POOL_SIZE > 0)
|
|
200
216
|
const char* val;
|
|
201
217
|
#endif
|
|
218
|
+
#ifndef __EMNAPI_WASI_THREADS__
|
|
202
219
|
uv_sem_t sem;
|
|
220
|
+
#endif
|
|
203
221
|
|
|
204
222
|
#if defined(EMNAPI_WORKER_POOL_SIZE) && EMNAPI_WORKER_POOL_SIZE > 0
|
|
205
223
|
nthreads = EMNAPI_WORKER_POOL_SIZE;
|
|
@@ -233,24 +251,33 @@ static void init_threads(void) {
|
|
|
233
251
|
QUEUE_INIT(&slow_io_pending_wq);
|
|
234
252
|
QUEUE_INIT(&run_slow_work_message);
|
|
235
253
|
|
|
254
|
+
#ifndef __EMNAPI_WASI_THREADS__
|
|
236
255
|
if (uv_sem_init(&sem, 0))
|
|
237
256
|
abort();
|
|
257
|
+
#endif
|
|
238
258
|
|
|
239
259
|
for (i = 0; i < nthreads; i++)
|
|
260
|
+
#ifndef __EMNAPI_WASI_THREADS__
|
|
240
261
|
if (uv_thread_create(threads + i, (uv_thread_cb) worker, &sem))
|
|
262
|
+
#else
|
|
263
|
+
if (uv_thread_create(threads + i, (uv_thread_cb) worker, NULL))
|
|
264
|
+
#endif
|
|
241
265
|
abort();
|
|
242
266
|
|
|
267
|
+
#ifndef __EMNAPI_WASI_THREADS__
|
|
243
268
|
for (i = 0; i < nthreads; i++)
|
|
244
269
|
uv_sem_wait(&sem);
|
|
245
270
|
|
|
246
271
|
uv_sem_destroy(&sem);
|
|
247
|
-
|
|
248
272
|
for (i = 0; i < nthreads; i++)
|
|
249
273
|
_emnapi_worker_unref(*(threads + i));
|
|
274
|
+
#else
|
|
275
|
+
_emnapi_tell_js_uvthreadpool(threads, nthreads);
|
|
276
|
+
#endif
|
|
250
277
|
}
|
|
251
278
|
|
|
252
279
|
|
|
253
|
-
#
|
|
280
|
+
#if !defined(_WIN32) && !defined(__wasi__)
|
|
254
281
|
static void reset_once(void) {
|
|
255
282
|
uv_once_t child_once = UV_ONCE_INIT;
|
|
256
283
|
memcpy(&once, &child_once, sizeof(child_once));
|
|
@@ -259,7 +286,7 @@ static void reset_once(void) {
|
|
|
259
286
|
|
|
260
287
|
|
|
261
288
|
static void init_once(void) {
|
|
262
|
-
#
|
|
289
|
+
#if !defined(_WIN32) && !defined(__wasi__)
|
|
263
290
|
/* Re-initialize the threadpool after fork.
|
|
264
291
|
* Note that this discards the global mutex and condition as well
|
|
265
292
|
* as the work queue.
|
|
@@ -280,7 +307,11 @@ void uv__work_submit(uv_loop_t* loop,
|
|
|
280
307
|
w->loop = loop;
|
|
281
308
|
w->work = work;
|
|
282
309
|
w->done = done;
|
|
310
|
+
// #ifdef __EMNAPI_WASI_THREADS__
|
|
311
|
+
// _emnapi_after_uvthreadpool_ready(post, &w->wq, kind);
|
|
312
|
+
// #else
|
|
283
313
|
post(&w->wq, kind);
|
|
314
|
+
// #endif
|
|
284
315
|
}
|
|
285
316
|
|
|
286
317
|
|
package/src/uv/unix/async.c
CHANGED
package/src/uv/unix/thread.c
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
#include <time.h>
|
|
7
7
|
#include "uv.h"
|
|
8
8
|
|
|
9
|
+
// #define CHECK_RET(the_call) \
|
|
10
|
+
// do { \
|
|
11
|
+
// int r = (the_call); \
|
|
12
|
+
// if (r) { \
|
|
13
|
+
// fprintf(stderr, #the_call ": %d\n", r); \
|
|
14
|
+
// abort(); \
|
|
15
|
+
// } \
|
|
16
|
+
// } while (0)
|
|
17
|
+
|
|
9
18
|
void uv_sem_post(sem_t* sem) {
|
|
10
19
|
if (sem_post(sem))
|
|
11
20
|
abort();
|
|
@@ -39,7 +48,7 @@ void uv_once(pthread_once_t* guard, void (*callback)(void)) {
|
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
int uv_mutex_init(uv_mutex_t* mutex) {
|
|
42
|
-
#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
|
|
51
|
+
#if defined(__wasi__) || defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
|
|
43
52
|
return pthread_mutex_init(mutex, NULL);
|
|
44
53
|
#else
|
|
45
54
|
pthread_mutexattr_t attr;
|
|
File without changes
|
|
File without changes
|