emnapi 0.34.0 → 0.36.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  cmake_minimum_required(VERSION 3.13)
2
2
 
3
- project(emnapi)
3
+ project(emnapi LANGUAGES C ASM)
4
4
 
5
5
  option(EMNAPI_INSTALL_SRC "EMNAPI_INSTALL_SRC" OFF)
6
6
  option(EMNAPI_FIND_NODE_ADDON_API "EMNAPI_FIND_NODE_ADDON_API" OFF)
@@ -50,10 +50,13 @@ else()
50
50
  endif()
51
51
 
52
52
  set(EMNAPI_BASIC_TARGET_NAME "emnapi-basic")
53
+ set(EMNAPI_BASIC_MT_TARGET_NAME "emnapi-basic-mt")
53
54
  set(EMNAPI_TARGET_NAME "emnapi")
54
55
  set(EMNAPI_MT_TARGET_NAME "emnapi-mt")
55
56
  set(DLMALLOC_TARGET_NAME "dlmalloc")
57
+ set(DLMALLOC_MT_TARGET_NAME "dlmalloc-mt")
56
58
  set(EMMALLOC_TARGET_NAME "emmalloc")
59
+ set(EMMALLOC_MT_TARGET_NAME "emmalloc-mt")
57
60
 
58
61
  if(EMNAPI_FIND_NODE_ADDON_API)
59
62
  execute_process(
@@ -82,11 +85,26 @@ if(IS_WASM32)
82
85
  "${CMAKE_CURRENT_SOURCE_DIR}/src/malloc/dlmalloc/dlmalloc.c"
83
86
  )
84
87
  target_compile_definitions(${DLMALLOC_TARGET_NAME} PRIVATE "PAGESIZE=65536")
88
+
89
+ add_library(${DLMALLOC_MT_TARGET_NAME} STATIC
90
+ ${MALLOC_PUBLIC_SOURCES}
91
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/malloc/dlmalloc/dlmalloc.c"
92
+ )
93
+ target_compile_options(${DLMALLOC_MT_TARGET_NAME} PUBLIC "-matomics" "-mbulk-memory")
94
+ target_compile_definitions(${DLMALLOC_MT_TARGET_NAME} PRIVATE "PAGESIZE=65536" "USE_LOCKS=1")
95
+
85
96
  add_library(${EMMALLOC_TARGET_NAME} STATIC
86
97
  ${MALLOC_PUBLIC_SOURCES}
87
98
  "${CMAKE_CURRENT_SOURCE_DIR}/src/malloc/emmalloc/emmalloc.c"
88
99
  )
89
100
  target_compile_definitions(${EMMALLOC_TARGET_NAME} PRIVATE "PAGESIZE=65536")
101
+
102
+ add_library(${EMMALLOC_MT_TARGET_NAME} STATIC
103
+ ${MALLOC_PUBLIC_SOURCES}
104
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/malloc/emmalloc/emmalloc.c"
105
+ )
106
+ target_compile_options(${EMMALLOC_MT_TARGET_NAME} PUBLIC "-matomics" "-mbulk-memory")
107
+ target_compile_definitions(${EMMALLOC_MT_TARGET_NAME} PRIVATE "PAGESIZE=65536" "__EMSCRIPTEN_SHARED_MEMORY__=1")
90
108
  endif()
91
109
 
92
110
  add_library(${EMNAPI_TARGET_NAME} STATIC ${EMNAPI_SRC} ${UV_SRC})
@@ -101,6 +119,22 @@ if(IS_EMSCRIPTEN)
101
119
  target_link_options(${EMNAPI_BASIC_TARGET_NAME} INTERFACE "--js-library=${EMNAPI_JS_LIB}")
102
120
  endif()
103
121
 
122
+ if(IS_WASM32 OR (CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi") OR (CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi-threads"))
123
+ set(EMNAPI_BUILD_BASIC_MT ON)
124
+ else()
125
+ set(EMNAPI_BUILD_BASIC_MT OFF)
126
+ endif()
127
+
128
+ if(EMNAPI_BUILD_BASIC_MT)
129
+ add_library(${EMNAPI_BASIC_MT_TARGET_NAME} STATIC
130
+ ${ENAPI_BASIC_SRC}
131
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/thread/async_worker_create.c"
132
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/thread/async_worker_init.S"
133
+ )
134
+ target_compile_options(${EMNAPI_BASIC_MT_TARGET_NAME} PUBLIC "-matomics" "-mbulk-memory")
135
+ target_include_directories(${EMNAPI_BASIC_MT_TARGET_NAME} PUBLIC ${EMNAPI_INCLUDE})
136
+ endif()
137
+
104
138
  if(IS_EMSCRIPTEN OR (CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi-threads"))
105
139
  set(EMNAPI_BUILD_MT ON)
106
140
  else()
@@ -141,9 +175,14 @@ if(LIB_ARCH)
141
175
  if(EMNAPI_BUILD_MT)
142
176
  install(TARGETS ${EMNAPI_MT_TARGET_NAME} DESTINATION "lib/${LIB_ARCH}")
143
177
  endif()
178
+ if(EMNAPI_BUILD_BASIC_MT)
179
+ install(TARGETS ${EMNAPI_BASIC_MT_TARGET_NAME} DESTINATION "lib/${LIB_ARCH}")
180
+ endif()
144
181
  if(IS_WASM32)
145
182
  install(TARGETS ${DLMALLOC_TARGET_NAME} DESTINATION "lib/${LIB_ARCH}")
183
+ install(TARGETS ${DLMALLOC_MT_TARGET_NAME} DESTINATION "lib/${LIB_ARCH}")
146
184
  install(TARGETS ${EMMALLOC_TARGET_NAME} DESTINATION "lib/${LIB_ARCH}")
185
+ install(TARGETS ${EMMALLOC_MT_TARGET_NAME} DESTINATION "lib/${LIB_ARCH}")
147
186
  endif()
148
187
  endif()
149
188
 
@@ -178,4 +217,7 @@ if(EMNAPI_INSTALL_SRC)
178
217
  install(DIRECTORY
179
218
  ${CMAKE_CURRENT_SOURCE_DIR}/src/malloc
180
219
  DESTINATION "src/${PROJECT_NAME}")
220
+ install(DIRECTORY
221
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/thread
222
+ DESTINATION "src/${PROJECT_NAME}")
181
223
  endif()
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  [![Build](https://github.com/toyobayashi/emnapi/actions/workflows/main.yml/badge.svg?branch=main)](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 with wasm support. [napi-rs support is comming soon](https://github.com/napi-rs/napi-rs/tree/emnapi).
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.
10
10
 
11
11
  This project aims to
12
12
 
@@ -25,16 +25,6 @@ See documentation for more details:
25
25
 
26
26
  [How to build Node-API official examples](https://github.com/toyobayashi/node-addon-examples)
27
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
-
38
28
  ## Prerequests
39
29
 
40
30
  You will need to install:
@@ -174,7 +164,7 @@ emcc -O3 \
174
164
  -I./node_modules/emnapi/include \
175
165
  -L./node_modules/emnapi/lib/wasm32-emscripten \
176
166
  --js-library=./node_modules/emnapi/dist/library_napi.js \
177
- -sEXPORTED_FUNCTIONS="['_malloc','_free']" \
167
+ -sEXPORTED_FUNCTIONS="['_napi_register_wasm_v1','_malloc','_free']" \
178
168
  -o hello.js \
179
169
  hello.c \
180
170
  -lemnapi
@@ -275,6 +265,9 @@ declare namespace Module {
275
265
  * napi_create_async_work and napi_create_threadsafe_function
276
266
  */
277
267
  nodeBinding?: typeof import('@emnapi/node-binding')
268
+
269
+ /** See Multithread part */
270
+ asyncWorkPoolSize?: number
278
271
  }
279
272
  export function emnapiInit (options: EmnapiInitOptions): any
280
273
  }
@@ -392,7 +385,7 @@ import { WASI } from '@tybys/wasm-util'
392
385
  import { Volume, createFsFromVolume } from 'memfs-browser'
393
386
 
394
387
  const fs = createFsFromVolume(Volume.fromJSON({ /* ... */ }))
395
- return instantiateNapiModule(fetch('./hello.wasm'), {
388
+ instantiateNapiModule(fetch('./hello.wasm'), {
396
389
  wasi: new WASI({ fs, /* ... */ })
397
390
  context: getDefaultContext(),
398
391
  overwriteImports (importObject) {
@@ -450,7 +443,7 @@ em++ -O3 \
450
443
  -I./node_modules/emnapi/include \
451
444
  -L./node_modules/emnapi/lib/wasm32-emscripten \
452
445
  --js-library=./node_modules/emnapi/dist/library_napi.js \
453
- -sEXPORTED_FUNCTIONS="['_malloc','_free']" \
446
+ -sEXPORTED_FUNCTIONS="['_napi_register_wasm_v1','_malloc','_free']" \
454
447
  -o hello.js \
455
448
  hello.cpp \
456
449
  -lemnapi
@@ -548,7 +541,7 @@ add_executable(hello hello.c)
548
541
  target_link_libraries(hello emnapi)
549
542
  if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
550
543
  target_link_options(hello PRIVATE
551
- "-sEXPORTED_FUNCTIONS=['_malloc','_free']"
544
+ "-sEXPORTED_FUNCTIONS=['_napi_register_wasm_v1','_malloc','_free']"
552
545
  )
553
546
  elseif(CMAKE_SYSTEM_NAME STREQUAL "WASI")
554
547
  set_target_properties(hello PROPERTIES SUFFIX ".wasm")
@@ -623,9 +616,9 @@ path = "src/main.rs"
623
616
  # crate-type = ["cdylib"]
624
617
 
625
618
  [dependencies]
626
- napi = { version = "2.10.13", default-features = false, features = ["napi8", "compat-mode"] }
619
+ napi = { version = "2.12.1", default-features = false, features = ["napi8"] }
627
620
  napi-sys = { version = "2.2.3", features = ["napi8"] }
628
- napi-derive = "2.10.0"
621
+ napi-derive = "2.12.2"
629
622
 
630
623
  [build-dependencies]
631
624
  napi-build = "2.0.1"
@@ -685,71 +678,134 @@ rustflags = [
685
678
  ```rust
686
679
  #![no_main]
687
680
 
688
- use napi::*;
689
-
690
- #[cfg(target_arch = "wasm32")]
691
- use napi::bindgen_prelude::*;
692
- #[cfg(target_arch = "wasm32")]
693
- use napi_sys::*;
681
+ use napi_derive::napi;
694
682
 
695
- #[macro_use]
696
- extern crate napi_derive;
697
-
698
- fn sum(a: i32, b: i32) -> i32 {
699
- a + b
683
+ #[napi]
684
+ fn fibonacci(n: u32) -> u32 {
685
+ match n {
686
+ 1 | 2 => 1,
687
+ _ => fibonacci(n - 1) + fibonacci(n - 2),
688
+ }
700
689
  }
690
+ ```
701
691
 
702
- #[js_function(2)]
703
- fn sum_js(ctx: CallContext) -> napi::Result<napi::JsNumber> {
704
- let arg0 = ctx.get::<napi::JsNumber>(0)?.get_int32()?;
705
- let arg1 = ctx.get::<napi::JsNumber>(1)?.get_int32()?;
706
- let ret = sum(arg0, arg1);
707
- ctx.env.create_int32(ret)
708
- }
692
+ </details>
709
693
 
710
- fn module_register(_env: napi::Env, mut exports: napi::JsObject) -> napi::Result<()> {
711
- exports.create_named_method("sum", sum_js)?;
694
+ <details>
695
+ <summary>index.js</summary><br />
712
696
 
713
- Ok(())
714
- }
697
+ ```js
698
+ const fs = require('fs')
699
+ const path = require('path')
700
+ const useWASI = false
715
701
 
716
- #[cfg(not(target_arch = "wasm32"))]
717
- #[module_exports]
718
- fn init(exports: napi::JsObject, env: napi::Env) -> napi::Result<()> {
719
- module_register(env, exports)
702
+ let wasi
703
+ if (useWASI) {
704
+ const { WASI } = require('wasi')
705
+ wasi = new WASI({ /* ... */ })
720
706
  }
721
707
 
722
- #[cfg(target_arch = "wasm32")]
723
- #[no_mangle]
724
- pub unsafe extern "C" fn napi_register_wasm_v1(env: napi_env, exports: napi_value) -> () {
725
- let env_object = napi::Env::from_raw(env);
726
- let exports_object = napi::JsObject::from_napi_value(env, exports).unwrap();
727
- module_register(env_object, exports_object).unwrap();
728
- }
708
+ const { instantiateNapiModule } = require('@emnapi/core')
709
+
710
+ const wasmBuffer = useWASI
711
+ ? fs.readFileSync(path.join(__dirname, './target/wasm32-wasi/release/binding.wasm'))
712
+ : fs.readFileSync(path.join(__dirname, './target/wasm32-unknown-unknown/release/binding.wasm'))
713
+
714
+ instantiateNapiModule(wasmBuffer, {
715
+ context: require('@emnapi/runtime').getDefaultContext(),
716
+ wasi,
717
+ beforeInit ({ instance }) {
718
+ for (const sym in instance.exports) {
719
+ if (sym.startsWith('__napi_register__')) {
720
+ instance.exports[sym]()
721
+ }
722
+ }
723
+ },
724
+ overwriteImports (importObject) {
725
+ importObject.env = {
726
+ ...importObject.env,
727
+ ...importObject.napi,
728
+ ...importObject.emnapi
729
+ }
730
+ }
731
+ }).then(({ instance, napiModule }) => {
732
+ const binding = napiModule.exports
733
+ // output: 5
734
+ console.log(binding.fibonacci(5))
735
+ })
729
736
  ```
730
737
 
731
738
  </details>
732
739
 
733
740
  ### Multithread
734
741
 
735
- If you want to use async work or thread safe functions,
736
- there are additional C source file need to be compiled and linking.
737
- Recommend use CMake directly.
742
+ Related API:
743
+
744
+ - [napi_*_async_work](https://nodejs.org/dist/latest/docs/api/n-api.html#napi_create_async_work)
745
+ - [napi_*_threadsafe_function](https://nodejs.org/dist/latest/docs/api/n-api.html#asynchronous-thread-safe-function-calls)
746
+
747
+ They are available in emnapi, but you need to know more details before you start to use them.
748
+ Now emnapi has 3 implementations of async work and 2 implementations of TSFN:
738
749
 
739
- **This is EXPERIMENTAL on non-emscripten.**
750
+ - Async work
751
+ - A. Libuv threadpool and pthread based implementation in C
752
+ - B. Single thread mock in JavaScript
753
+ - C. Web worker based implementation in C (stack allocation) and JavaScript
754
+ - TSFN
755
+ - D. Libuv and pthread based implementation in C
756
+ - E. Web worker based implementation in JavaScript
757
+
758
+ | | Library to Link | `wasm32-emscripten` | `wasm32` | `wasm32-wasi` | `wasm32-wasi-threads` |
759
+ |---|------------------------|---------------------|----------|---------------|-----------------------|
760
+ | A | libemnapi-mt.a | ✅ | ❌ | ❌ | ✅ |
761
+ | B | libemnapi-basic(-mt).a | ✅ | ✅ | ✅ | ✅ |
762
+ | C | libemnapi-basic-mt.a | ❌ | ✅ | ❌ | ✅ |
763
+ | D | libemnapi-mt.a | ✅ | ❌ | ❌ | ✅ |
764
+ | E | libemnapi-basic(-mt).a | ✅ | ✅ | ✅ | ✅ |
765
+
766
+ There are some limitations on browser about wasi-libc's pthread implementation, for example
767
+ `pthread_mutex_lock` may call `__builtin_wasm_memory_atomic_wait32`(`memory.atomic.wait32`)
768
+ which is disallowed in browser JS main thread. While Emscripten's pthread implementation
769
+ has considered usage in browser. If you need to run your addon with multithreaded features on browser,
770
+ we recommend you use Emscripten A & D, or bare wasm32 C & E.
771
+
772
+ Note: For browsers, all the multithreaded features relying on Web Workers (Emscripten pthread also relying on Web Workers)
773
+ require cross-origin isolation to enable `SharedArrayBuffer`. You can make a page cross-origin isolated
774
+ by serving the page with these headers:
775
+
776
+ ```
777
+ Cross-Origin-Embedder-Policy: require-corp
778
+ Cross-Origin-Opener-Policy: same-origin
779
+ ```
780
+
781
+ #### About Prebuilt Libraries
782
+
783
+ Prebuilt libraries can be found in the `lib` directory in `emnapi` npm package.
784
+
785
+ | Library | Description | `wasm32-emscripten` | `wasm32` | `wasm32-wasi` | `wasm32-wasi-threads` |
786
+ |----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------|----------|---------------|-----------------------------------------|
787
+ | libemnapi.a | no atomics feature.<br/><br/> no libuv port.<br/><br/> `napi_*_async_work` and `napi_*_threadsafe_function` always return `napi_generic_failure`. | ✅ | ✅ | ✅ | waiting wasi-sdk release thread support |
788
+ | libemnapi-mt.a | atomics feature enabled.<br/><br/> `napi_*_async_work` and `napi_*_threadsafe_function` are based on pthread and libuv port. | ✅ | ❌ | ❌ | waiting wasi-sdk release thread support |
789
+ | libemnapi-basic.a | no atomics feature.<br/><br/> no libuv port.<br/><br/> `napi_*_async_work` and `napi_*_threadsafe_function` are imported from JavaScript land. | ✅ | ✅ | ✅ | waiting wasi-sdk release thread support |
790
+ | libemnapi-basic-mt.a | atomics feature enabled.<br/><br/> no libuv port.<br/><br/> `napi_*_async_work` and `napi_*_threadsafe_function` are imported from JavaScript land.<br/><br/> include `emnapi_async_worker_create` and `emnapi_async_worker_init` for WebWorker based async work implementation. | ❌ | ✅ | ✅ | waiting wasi-sdk release thread support |
791
+ | libdlmalloc.a | no atomics feature, no thread safe garanteed. | ❌ | ✅ | ❌ | ❌ |
792
+ | libdlmalloc-mt.a | atomics feature enabled, thread safe. | ❌ | ✅ | ❌ | ❌ |
793
+ | libemmalloc.a | no atomics feature, no thread safe garanteed. | ❌ | ✅ | ❌ | ❌ |
794
+ | libemmalloc-mt.a | atomics feature enabled, thread safe. | ❌ | ✅ | ❌ | ❌ |
795
+
796
+ #### Usage
740
797
 
741
798
  ```cmake
742
799
  add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/node_modules/emnapi")
743
800
 
744
801
  add_executable(hello hello.c)
745
802
 
746
- target_link_libraries(hello emnapi-mt)
747
-
748
803
  if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
804
+ target_link_libraries(hello emnapi-mt)
749
805
  target_compile_options(hello PRIVATE "-pthread")
750
806
  target_link_options(hello PRIVATE
751
807
  "-sALLOW_MEMORY_GROWTH=1"
752
- "-sEXPORTED_FUNCTIONS=['_malloc','_free']"
808
+ "-sEXPORTED_FUNCTIONS=['_napi_register_wasm_v1','_malloc','_free']"
753
809
  "-pthread"
754
810
  "-sPTHREAD_POOL_SIZE=4"
755
811
  # try to specify stack size if you experience pthread errors
@@ -758,6 +814,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
758
814
  )
759
815
  elseif(CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi-threads")
760
816
  # Experimental
817
+ target_link_libraries(hello emnapi-mt)
761
818
  set_target_properties(hello PROPERTIES SUFFIX ".wasm")
762
819
  target_compile_options(hello PRIVATE "-fno-exceptions" "-pthread")
763
820
  target_link_options(hello PRIVATE
@@ -771,6 +828,19 @@ elseif(CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi-threads")
771
828
  "-Wl,--import-undefined"
772
829
  "-Wl,--export-table"
773
830
  )
831
+ elseif((CMAKE_C_COMPILER_TARGET STREQUAL "wasm32") OR (CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-unknown-unknown"))
832
+ target_link_libraries(hello emnapi-basic-mt)
833
+ set_target_properties(hello PROPERTIES SUFFIX ".wasm")
834
+ target_compile_options(hello PRIVATE "-fno-exceptions" "-matomics" "-mbulk-memory")
835
+ target_link_options(hello PRIVATE
836
+ "-nostdlib"
837
+ "-Wl,--no-entry"
838
+ "-Wl,--export=napi_register_wasm_v1"
839
+ "-Wl,--export=emnapi_async_worker_create"
840
+ "-Wl,--export=emnapi_async_worker_init"
841
+ "-Wl,--import-memory,--shared-memory,--max-memory=2147483648,--import-undefined"
842
+ "-Wl,--export-dynamic,--export=malloc,--export=free,--export-table"
843
+ )
774
844
  endif()
775
845
  ```
776
846
 
@@ -784,6 +854,11 @@ cmake -DCMAKE_TOOLCHAIN_FILE=$WASI_SDK_PATH/share/cmake/wasi-sdk-pthread.cmake \
784
854
  -DCMAKE_BUILD_TYPE=Release \
785
855
  -G Ninja -H. -Bbuild
786
856
 
857
+ cmake -DCMAKE_TOOLCHAIN_FILE=node_modules/emnapi/cmake/wasm32.cmake \
858
+ -DLLVM_PREFIX=$WASI_SDK_PATH \
859
+ -DCMAKE_BUILD_TYPE=Release \
860
+ -G Ninja -H. -Bbuild
861
+
787
862
  cmake --build build
788
863
  ```
789
864
 
@@ -793,6 +868,15 @@ And additional work is required during instantiating wasm compiled with non-emsc
793
868
  // emnapi main thread (could be in a Worker)
794
869
  instantiateNapiModule(input, {
795
870
  context: getDefaultContext(),
871
+ /**
872
+ * emscripten
873
+ * 0: no effect
874
+ * > 0: the same effect to UV_THREADPOOL_SIZE
875
+ * non-emscripten
876
+ * 0: single thread mock
877
+ * > 0 schedule async work in web worker
878
+ */
879
+ asyncWorkPoolSize: 4, // 0: single thread mock, > 0: schedule async work in web worker
796
880
  wasi: new WASI(/* ... */),
797
881
  // reuseWorker: true,
798
882
  onCreateWorker () {
@@ -890,17 +974,30 @@ instantiateNapiModule(input, {
890
974
 
891
975
  ### `-DEMNAPI_WORKER_POOL_SIZE=4`
892
976
 
893
- This is [`UV_THREADPOOL_SIZE`](http://docs.libuv.org/en/v1.x/threadpool.html?highlight=UV_THREADPOOL_SIZE) equivalent at compile time, if not predefined, emnapi will read `UV_THREADPOOL_SIZE` from Emscripten [environment variable](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#interacting-with-code-environment-variables) at runtime, you can set `UV_THREADPOOL_SIZE` like this:
977
+ This is [`UV_THREADPOOL_SIZE`](http://docs.libuv.org/en/v1.x/threadpool.html?highlight=UV_THREADPOOL_SIZE) equivalent at compile time, if not predefined, emnapi will read `asyncWorkPoolSize` option or `UV_THREADPOOL_SIZE` from Emscripten [environment variable](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#interacting-with-code-environment-variables) at runtime:
894
978
 
895
979
  ```js
980
+ Module.init({
981
+ // ...
982
+ asyncWorkPoolSize: 2
983
+ })
984
+
985
+ // if asyncWorkPoolSize is not specified
896
986
  Module.preRun = Module.preRun || [];
897
987
  Module.preRun.push(function () {
898
988
  if (typeof ENV !== 'undefined') {
899
989
  ENV.UV_THREADPOOL_SIZE = '2';
900
990
  }
901
991
  });
992
+ ```
902
993
 
994
+ ```js
903
995
  // wasi
996
+ instantiateNapiModule({
997
+ // ...
998
+ asyncWorkPoolSize: 2
999
+ })
1000
+ // if asyncWorkPoolSize is not specified
904
1001
  new WASI({
905
1002
  env: {
906
1003
  UV_THREADPOOL_SIZE: '2'
@@ -908,7 +1005,7 @@ new WASI({
908
1005
  })
909
1006
  ```
910
1007
 
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.
1008
+ It represent max of `EMNAPI_WORKER_POOL_SIZE` async work (`napi_queue_async_work`) can be executed in parallel. Default is not defined.
912
1009
 
913
1010
  You can set both `PTHREAD_POOL_SIZE` and `EMNAPI_WORKER_POOL_SIZE` to `number of CPU cores` in general.
914
1011
  If you use another library function which may create `N` child threads in async work,
@@ -16,10 +16,12 @@ endif()
16
16
 
17
17
  set(CMAKE_C_COMPILER ${LLVM_PREFIX}/bin/clang${WASM_HOST_EXE_SUFFIX})
18
18
  set(CMAKE_CXX_COMPILER ${LLVM_PREFIX}/bin/clang++${WASM_HOST_EXE_SUFFIX})
19
+ set(CMAKE_ASM_COMPILER ${LLVM_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX})
19
20
  set(CMAKE_AR ${LLVM_PREFIX}/bin/llvm-ar${WASM_HOST_EXE_SUFFIX})
20
21
  set(CMAKE_RANLIB ${LLVM_PREFIX}/bin/llvm-ranlib${WASM_HOST_EXE_SUFFIX})
21
22
  set(CMAKE_C_COMPILER_TARGET ${triple})
22
23
  set(CMAKE_CXX_COMPILER_TARGET ${triple})
24
+ set(CMAKE_ASM_COMPILER_TARGET ${triple})
23
25
 
24
26
  # Don't look in the sysroot for executables to run during the build
25
27
  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
@@ -1,37 +1,33 @@
1
1
  {{{ ((DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.indexOf("$emnapiInit") === -1 ? DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push("$emnapiInit") : undefined), "") }}}
2
2
  {{{ ((EXPORTED_RUNTIME_METHODS.indexOf("emnapiInit") === -1 ? EXPORTED_RUNTIME_METHODS.push("emnapiInit") : undefined), "") }}}
3
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
4
- function emnapiCreateIdGenerator() {
5
- var obj = {
6
- nextId: 1,
7
- list: [],
8
- generate: function () {
9
- var id;
10
- if (obj.list.length) {
11
- id = obj.list.shift();
12
- }
13
- else {
14
- id = obj.nextId;
15
- obj.nextId++;
16
- }
17
- return id;
18
- },
19
- reuse: function (id) {
20
- obj.list.push(id);
21
- }
22
- };
23
- return obj;
24
- }
25
- var emnapiAsyncWork = {
3
+ var emnapiAWST = {
26
4
  idGen: {},
27
5
  values: [undefined],
28
6
  queued: new Set(),
29
7
  pending: [],
30
8
  init: function () {
31
- emnapiAsyncWork.idGen = emnapiCreateIdGenerator();
32
- emnapiAsyncWork.values = [undefined];
33
- emnapiAsyncWork.queued = new Set();
34
- emnapiAsyncWork.pending = [];
9
+ var idGen = {
10
+ nextId: 1,
11
+ list: [],
12
+ generate: function () {
13
+ var id;
14
+ if (idGen.list.length) {
15
+ id = idGen.list.shift();
16
+ }
17
+ else {
18
+ id = idGen.nextId;
19
+ idGen.nextId++;
20
+ }
21
+ return id;
22
+ },
23
+ reuse: function (id) {
24
+ idGen.list.push(id);
25
+ }
26
+ };
27
+ emnapiAWST.idGen = idGen;
28
+ emnapiAWST.values = [undefined];
29
+ emnapiAWST.queued = new Set();
30
+ emnapiAWST.pending = [];
35
31
  },
36
32
  create: function (env, resource, resourceName, execute, complete, data) {
37
33
  var asyncId = 0;
@@ -41,12 +37,11 @@ var emnapiAsyncWork = {
41
37
  asyncId = asyncContext.asyncId;
42
38
  triggerAsyncId = asyncContext.triggerAsyncId;
43
39
  }
44
- var id = emnapiAsyncWork.idGen.generate();
45
- emnapiAsyncWork.values[id] = {
40
+ var id = emnapiAWST.idGen.generate();
41
+ emnapiAWST.values[id] = {
46
42
  env: env,
47
43
  id: id,
48
44
  resource: resource,
49
- resourceName: resourceName,
50
45
  asyncId: asyncId,
51
46
  triggerAsyncId: triggerAsyncId,
52
47
  status: 0,
@@ -62,6 +57,8 @@ var emnapiAsyncWork = {
62
57
  var env = work.env;
63
58
  var data = work.data;
64
59
  var callback = function () {
60
+ if (!complete)
61
+ return;
65
62
  var envObject = emnapiCtx.envStore.get(env);
66
63
  var scope = emnapiCtx.openScope(envObject);
67
64
  try {
@@ -84,16 +81,16 @@ var emnapiAsyncWork = {
84
81
  }
85
82
  },
86
83
  queue: function (id) {
87
- var work = emnapiAsyncWork.values[id];
84
+ var work = emnapiAWST.values[id];
88
85
  if (!work)
89
86
  return;
90
87
  if (work.status === 0) {
91
88
  work.status = 1;
92
- if (emnapiAsyncWork.queued.size >= 4) {
93
- emnapiAsyncWork.pending.push(id);
89
+ if (emnapiAWST.queued.size >= (Math.abs(emnapiAsyncWorkPoolSize) || 4)) {
90
+ emnapiAWST.pending.push(id);
94
91
  return;
95
92
  }
96
- emnapiAsyncWork.queued.add(id);
93
+ emnapiAWST.queued.add(id);
97
94
  var env_1 = work.env;
98
95
  var data_1 = work.data;
99
96
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -101,25 +98,29 @@ var emnapiAsyncWork = {
101
98
  work.status = 2;
102
99
  emnapiCtx.feature.setImmediate(function () {
103
100
  {{{ makeDynCall('vpp', 'execute') }}}(env_1, data_1);
104
- emnapiAsyncWork.queued.delete(id);
101
+ emnapiAWST.queued.delete(id);
105
102
  work.status = 3;
106
- emnapiAsyncWork.callComplete(work, 0 /* napi_status.napi_ok */);
107
- if (emnapiAsyncWork.pending.length > 0) {
108
- var nextWorkId = emnapiAsyncWork.pending.shift();
109
- emnapiAsyncWork.values[nextWorkId].status = 0;
110
- emnapiAsyncWork.queue(nextWorkId);
103
+ emnapiCtx.feature.setImmediate(function () {
104
+ emnapiAWST.callComplete(work, 0 /* napi_status.napi_ok */);
105
+ });
106
+ if (emnapiAWST.pending.length > 0) {
107
+ var nextWorkId = emnapiAWST.pending.shift();
108
+ emnapiAWST.values[nextWorkId].status = 0;
109
+ emnapiAWST.queue(nextWorkId);
111
110
  }
112
111
  });
113
112
  }
114
113
  },
115
114
  cancel: function (id) {
116
- var index = emnapiAsyncWork.pending.indexOf(id);
115
+ var index = emnapiAWST.pending.indexOf(id);
117
116
  if (index !== -1) {
118
- var work = emnapiAsyncWork.values[id];
119
- if (work && (work.status === 1)) {
120
- work.status = 4;
121
- emnapiAsyncWork.pending.splice(index, 1);
122
- emnapiAsyncWork.callComplete(work, 11 /* napi_status.napi_cancelled */);
117
+ var work_1 = emnapiAWST.values[id];
118
+ if (work_1 && (work_1.status === 1)) {
119
+ work_1.status = 4;
120
+ emnapiAWST.pending.splice(index, 1);
121
+ emnapiCtx.feature.setImmediate(function () {
122
+ emnapiAWST.callComplete(work_1, 11 /* napi_status.napi_cancelled */);
123
+ });
123
124
  return 0 /* napi_status.napi_ok */;
124
125
  }
125
126
  else {
@@ -129,7 +130,7 @@ var emnapiAsyncWork = {
129
130
  return 9 /* napi_status.napi_generic_failure */;
130
131
  },
131
132
  remove: function (id) {
132
- var work = emnapiAsyncWork.values[id];
133
+ var work = emnapiAWST.values[id];
133
134
  if (!work)
134
135
  return;
135
136
  if (emnapiNodeBinding) {
@@ -138,68 +139,11 @@ var emnapiAsyncWork = {
138
139
  triggerAsyncId: work.triggerAsyncId
139
140
  });
140
141
  }
141
- emnapiAsyncWork.values[id] = undefined;
142
- emnapiAsyncWork.idGen.reuse(id);
142
+ emnapiAWST.values[id] = undefined;
143
+ emnapiAWST.idGen.reuse(id);
143
144
  }
144
145
  };
145
- function _napi_create_async_work(env, resource, resource_name, execute, complete, data, result) {
146
- if (env == 0)
147
- return 1 /* napi_status.napi_invalid_arg */;
148
- var envObject = emnapiCtx.envStore.get(env);
149
- if (execute == 0)
150
- return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
151
- if (result == 0)
152
- return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
153
- var resourceObject;
154
- if (resource) {
155
- resourceObject = Object(emnapiCtx.handleStore.get(resource).value);
156
- }
157
- else {
158
- resourceObject = {};
159
- }
160
- if (resource_name == 0)
161
- return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
162
- var resourceName = String(emnapiCtx.handleStore.get(resource_name).value);
163
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
164
- var id = emnapiAsyncWork.create(env, resourceObject, resourceName, execute, complete, data);
165
- {{{ makeSetValue('result', 0, 'id', '*') }}};
166
- return envObject.clearLastError();
167
- }
168
- function _napi_delete_async_work(env, work) {
169
- if (env == 0)
170
- return 1 /* napi_status.napi_invalid_arg */;
171
- var envObject = emnapiCtx.envStore.get(env);
172
- if (work == 0)
173
- return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
174
- emnapiAsyncWork.remove(work);
175
- return envObject.clearLastError();
176
- }
177
- function _napi_queue_async_work(env, work) {
178
- if (env == 0)
179
- return 1 /* napi_status.napi_invalid_arg */;
180
- var envObject = emnapiCtx.envStore.get(env);
181
- if (work == 0)
182
- return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
183
- emnapiAsyncWork.queue(work);
184
- return envObject.clearLastError();
185
- }
186
- function _napi_cancel_async_work(env, work) {
187
- if (env == 0)
188
- return 1 /* napi_status.napi_invalid_arg */;
189
- var envObject = emnapiCtx.envStore.get(env);
190
- if (work == 0)
191
- return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
192
- var status = emnapiAsyncWork.cancel(work);
193
- if (status === 0 /* napi_status.napi_ok */)
194
- return envObject.clearLastError();
195
- return envObject.setLastError(status);
196
- }
197
- emnapiImplementHelper('$emnapiCreateIdGenerator', undefined, emnapiCreateIdGenerator, []);
198
- emnapiDefineVar('$emnapiAsyncWork', emnapiAsyncWork, ['$emnapiCreateIdGenerator'], 'emnapiAsyncWork.init();');
199
- emnapiImplement('napi_create_async_work', 'ippppppp', _napi_create_async_work, ['$emnapiAsyncWork']);
200
- emnapiImplement('napi_delete_async_work', 'ipp', _napi_delete_async_work, ['$emnapiAsyncWork']);
201
- emnapiImplement('napi_queue_async_work', 'ipp', _napi_queue_async_work, ['$emnapiAsyncWork']);
202
- emnapiImplement('napi_cancel_async_work', 'ipp', _napi_cancel_async_work, ['$emnapiAsyncWork']);
146
+ emnapiDefineVar('$emnapiAWST', emnapiAWST, ['$emnapiAsyncWorkPoolSize'], 'emnapiAWST.init();');
203
147
  function _emnapi_create_memory_view(env, typedarray_type, external_data, byte_length, finalize_cb, finalize_hint, result
204
148
  // @ts-expect-error
205
149
  ) {
@@ -1277,8 +1221,8 @@ function napi_create_reference(env, value, initial_refcount, result) {
1277
1221
  if (result == 0)
1278
1222
  return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
1279
1223
  var handle = emnapiCtx.handleStore.get(value);
1280
- if (!(handle.isObject() || handle.isFunction())) {
1281
- return envObject.setLastError(2 /* napi_status.napi_object_expected */);
1224
+ if (!(handle.isObject() || handle.isFunction() || handle.isSymbol())) {
1225
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
1282
1226
  }
1283
1227
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1284
1228
  var ref = emnapiCtx.createReference(envObject, handle.id, initial_refcount >>> 0, 1 /* Ownership.kUserland */);
@@ -4736,17 +4680,22 @@ function napi_create_string_latin1(env, str, length, result) {
4736
4680
  if (env == 0)
4737
4681
  return 1 /* napi_status.napi_invalid_arg */;
4738
4682
  var envObject = emnapiCtx.envStore.get(env);
4683
+ {{{ from64('length') }}};
4684
+ var autoLength = length === -1;
4685
+ length = length >>> 0;
4686
+ if (length !== 0) {
4687
+ if (str == 0)
4688
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
4689
+ }
4739
4690
  if (result == 0)
4740
4691
  return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
4741
4692
  {{{ from64('str') }}};
4742
- {{{ from64('length') }}};
4743
- length = length >>> 0;
4744
- if (!((length === 0xffffffff) || (length <= 2147483647)) || (!str)) {
4693
+ if (!(autoLength || (length <= 2147483647))) {
4745
4694
  return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
4746
4695
  }
4747
4696
  var latin1String = '';
4748
4697
  var len = 0;
4749
- if (length === -1) {
4698
+ if (autoLength) {
4750
4699
  while (true) {
4751
4700
  var ch = {{{ makeGetValue('str', 0, 'u8') }}};
4752
4701
  if (!ch)
@@ -4775,11 +4724,17 @@ function napi_create_string_utf16(env, str, length, result) {
4775
4724
  if (env == 0)
4776
4725
  return 1 /* napi_status.napi_invalid_arg */;
4777
4726
  var envObject = emnapiCtx.envStore.get(env);
4727
+ {{{ from64('length') }}};
4728
+ var autoLength = length === -1;
4729
+ var sizelength = length >>> 0;
4730
+ if (length !== 0) {
4731
+ if (str == 0)
4732
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
4733
+ }
4778
4734
  if (result == 0)
4779
4735
  return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
4780
4736
  {{{ from64('str') }}};
4781
- {{{ from64('length') }}};
4782
- if (((length < -1) || (length > 2147483647)) || (!str)) {
4737
+ if (!(autoLength || (sizelength <= 2147483647))) {
4783
4738
  return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
4784
4739
  }
4785
4740
  var utf16String = emnapiUtf16ToString(str, length);
@@ -4793,11 +4748,17 @@ function napi_create_string_utf8(env, str, length, result) {
4793
4748
  if (env == 0)
4794
4749
  return 1 /* napi_status.napi_invalid_arg */;
4795
4750
  var envObject = emnapiCtx.envStore.get(env);
4751
+ {{{ from64('length') }}};
4752
+ var autoLength = length === -1;
4753
+ var sizelength = length >>> 0;
4754
+ if (length !== 0) {
4755
+ if (str == 0)
4756
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
4757
+ }
4796
4758
  if (result == 0)
4797
4759
  return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
4798
4760
  {{{ from64('str') }}};
4799
- {{{ from64('length') }}};
4800
- if (((length < -1) || (length > 2147483647)) || (!str)) {
4761
+ if (!(autoLength || (sizelength <= 2147483647))) {
4801
4762
  return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
4802
4763
  }
4803
4764
  var utf8String = emnapiUtf8ToString(str, length);
@@ -5392,7 +5353,13 @@ function node_api_symbol_for(env, utf8description, length, result) {
5392
5353
  {{{ from64('length') }}};
5393
5354
  {{{ from64('utf8description') }}};
5394
5355
  {{{ from64('result') }}};
5395
- if (((length < -1) || (length > 2147483647)) || (!utf8description)) {
5356
+ var autoLength = length === -1;
5357
+ var sizelength = length >>> 0;
5358
+ if (length !== 0) {
5359
+ if (utf8description == 0)
5360
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
5361
+ }
5362
+ if (!(autoLength || (sizelength <= 2147483647))) {
5396
5363
  return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
5397
5364
  }
5398
5365
  var descriptionString = emnapiUtf8ToString(utf8description, length);
@@ -5468,6 +5435,62 @@ emnapiImplement('napi_get_boolean', 'ipip', napi_get_boolean);
5468
5435
  emnapiImplement('napi_get_global', 'ipp', napi_get_global);
5469
5436
  emnapiImplement('napi_get_null', 'ipp', napi_get_null);
5470
5437
  emnapiImplement('napi_get_undefined', 'ipp', napi_get_undefined);
5438
+ function _napi_create_async_work(env, resource, resource_name, execute, complete, data, result) {
5439
+ if (env == 0)
5440
+ return 1 /* napi_status.napi_invalid_arg */;
5441
+ var envObject = emnapiCtx.envStore.get(env);
5442
+ if (execute == 0)
5443
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
5444
+ if (result == 0)
5445
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
5446
+ var resourceObject;
5447
+ if (resource) {
5448
+ resourceObject = Object(emnapiCtx.handleStore.get(resource).value);
5449
+ }
5450
+ else {
5451
+ resourceObject = {};
5452
+ }
5453
+ if (resource_name == 0)
5454
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
5455
+ var resourceName = String(emnapiCtx.handleStore.get(resource_name).value);
5456
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5457
+ var id = emnapiAWST.create(env, resourceObject, resourceName, execute, complete, data);
5458
+ {{{ makeSetValue('result', 0, 'id', '*') }}};
5459
+ return envObject.clearLastError();
5460
+ }
5461
+ function _napi_delete_async_work(env, work) {
5462
+ if (env == 0)
5463
+ return 1 /* napi_status.napi_invalid_arg */;
5464
+ var envObject = emnapiCtx.envStore.get(env);
5465
+ if (work == 0)
5466
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
5467
+ emnapiAWST.remove(work);
5468
+ return envObject.clearLastError();
5469
+ }
5470
+ function _napi_queue_async_work(env, work) {
5471
+ if (env == 0)
5472
+ return 1 /* napi_status.napi_invalid_arg */;
5473
+ var envObject = emnapiCtx.envStore.get(env);
5474
+ if (work == 0)
5475
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
5476
+ emnapiAWST.queue(work);
5477
+ return envObject.clearLastError();
5478
+ }
5479
+ function _napi_cancel_async_work(env, work) {
5480
+ if (env == 0)
5481
+ return 1 /* napi_status.napi_invalid_arg */;
5482
+ var envObject = emnapiCtx.envStore.get(env);
5483
+ if (work == 0)
5484
+ return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
5485
+ var status = emnapiAWST.cancel(work);
5486
+ if (status === 0 /* napi_status.napi_ok */)
5487
+ return envObject.clearLastError();
5488
+ return envObject.setLastError(status);
5489
+ }
5490
+ emnapiImplement('napi_create_async_work', 'ippppppp', _napi_create_async_work, ['$emnapiAWST']);
5491
+ emnapiImplement('napi_delete_async_work', 'ipp', _napi_delete_async_work, ['$emnapiAWST']);
5492
+ emnapiImplement('napi_queue_async_work', 'ipp', _napi_queue_async_work, ['$emnapiAWST']);
5493
+ emnapiImplement('napi_cancel_async_work', 'ipp', _napi_cancel_async_work, ['$emnapiAWST']);
5471
5494
  mergeInto(LibraryManager.library, {
5472
5495
  _emnapi_worker_unref__sig: 'vp',
5473
5496
  _emnapi_worker_unref__deps: ['$PThread'],
@@ -5618,6 +5641,7 @@ emnapiDefineVar('$emnapiModule', {
5618
5641
  loaded: false,
5619
5642
  filename: ''
5620
5643
  });
5644
+ emnapiDefineVar('$emnapiAsyncWorkPoolSize', 0);
5621
5645
  function emnapiInit(options) {
5622
5646
  if (emnapiModule.loaded)
5623
5647
  return emnapiModule.exports;
@@ -5639,6 +5663,18 @@ function emnapiInit(options) {
5639
5663
  }
5640
5664
  emnapiNodeBinding = nodeBinding;
5641
5665
  }
5666
+ if ('asyncWorkPoolSize' in options) {
5667
+ if (typeof options.asyncWorkPoolSize !== 'number') {
5668
+ throw new TypeError('options.asyncWorkPoolSize must be a integer');
5669
+ }
5670
+ emnapiAsyncWorkPoolSize = options.asyncWorkPoolSize >> 0;
5671
+ if (emnapiAsyncWorkPoolSize > 1024) {
5672
+ emnapiAsyncWorkPoolSize = 1024;
5673
+ }
5674
+ else if (emnapiAsyncWorkPoolSize < -1024) {
5675
+ emnapiAsyncWorkPoolSize = -1024;
5676
+ }
5677
+ }
5642
5678
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
5643
5679
  var envObject = emnapiModule.envObject || (emnapiModule.envObject = emnapiCtx.createEnv(function (cb) { return {{{ makeDynCall('vppp', 'cb') }}}; }, function (cb) { return {{{ makeDynCall('vp', 'cb') }}}; }));
5644
5680
  var scope = emnapiCtx.openScope(envObject);
@@ -5660,7 +5696,11 @@ function emnapiInit(options) {
5660
5696
  delete emnapiModule.envObject;
5661
5697
  return emnapiModule.exports;
5662
5698
  }
5663
- emnapiImplementHelper('$emnapiInit', undefined, emnapiInit, ['$emnapiModule', '$emnapiCtx', '$emnapiNodeBinding', 'napi_register_wasm_v1']);
5699
+ emnapiImplementHelper('$emnapiInit', undefined, emnapiInit, ['$emnapiModule', '$emnapiCtx', '$emnapiNodeBinding', '$emnapiAsyncWorkPoolSize', 'napi_register_wasm_v1']);
5700
+ function __emnapi_async_work_pool_size() {
5701
+ return Math.abs(emnapiAsyncWorkPoolSize);
5702
+ }
5703
+ emnapiImplementInternal('_emnapi_async_work_pool_size', 'i', __emnapi_async_work_pool_size, ['$emnapiAsyncWorkPoolSize']);
5664
5704
  function __emnapi_get_filename(buf, len) {
5665
5705
  if (!buf) {
5666
5706
  return lengthBytesUTF8(emnapiModule.filename);
@@ -5746,6 +5786,8 @@ mergeInto(LibraryManager.library, {
5746
5786
  return UTF8ToString(ptr);
5747
5787
  }
5748
5788
  length = length >>> 0;
5789
+ if (!length)
5790
+ return '';
5749
5791
  return emnapiUtf8Decoder.decode({{{ getUnsharedTextDecoderView('HEAPU8', 'ptr', 'ptr + length') }}});
5750
5792
  },
5751
5793
  $emnapiUtf16leDecoder__postset: 'emnapiUtf16leDecoder();',
@@ -5789,6 +5831,8 @@ mergeInto(LibraryManager.library, {
5789
5831
  return UTF16ToString(ptr);
5790
5832
  }
5791
5833
  length = length >>> 0;
5834
+ if (!length)
5835
+ return '';
5792
5836
  return emnapiUtf16leDecoder.decode({{{ getUnsharedTextDecoderView('HEAPU8', 'ptr', 'ptr + length * 2') }}});
5793
5837
  }
5794
5838
  });
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
- emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.32 (eab98adf462c39f3c31d348331c4830bcaa36949)
2
- clang version 17.0.0 (https://github.com/llvm/llvm-project df82394e7a2d06506718cafa347bf7827c79fc4f)
1
+ emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.34 (57b21b8fdcbe3ebb523178b79465254668eab408)
2
+ clang version 17.0.0 (https://github.com/llvm/llvm-project a031f72187ce495b9faa4ccf99b1e901a3872f4b)
3
3
  Target: wasm32-unknown-emscripten
4
4
  Thread model: posix
5
5
  InstalledDir: /home/runner/work/emnapi/emnapi/emsdk-cache/emsdk-main/upstream/bin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emnapi",
3
- "version": "0.34.0",
3
+ "version": "0.36.0",
4
4
  "description": "Node-API implementation for Emscripten",
5
5
  "main": "index.js",
6
6
  "devDependencies": {
@@ -0,0 +1,76 @@
1
+ #include <stddef.h>
2
+ #include <stdint.h>
3
+
4
+ void* calloc(size_t n, size_t size);
5
+
6
+ extern unsigned char __heap_base;
7
+ extern unsigned char __data_end;
8
+ extern unsigned char __global_base;
9
+ extern __attribute__((__weak__)) unsigned char __stack_high;
10
+ extern __attribute__((__weak__)) unsigned char __stack_low;
11
+
12
+ #define ROUND_UP(x, ALIGNMENT) (((x)+ALIGNMENT-1)&-ALIGNMENT)
13
+ #define STACK_ALIGN 16
14
+ #define DEFAULT_STACK_MAX (8<<20)
15
+
16
+ struct worker_args {
17
+ void* stack_base;
18
+ void* tls_base;
19
+ };
20
+
21
+ extern void __wasm_init_tls(void*);
22
+
23
+ void* __copy_tls(unsigned char *mem) {
24
+ size_t tls_align = __builtin_wasm_tls_align();
25
+ volatile void* tls_base = __builtin_wasm_tls_base();
26
+ mem += tls_align;
27
+ mem -= (uintptr_t)mem & (tls_align-1);
28
+ __wasm_init_tls(mem);
29
+ __asm__("local.get %0\n"
30
+ "global.set __tls_base\n"
31
+ :: "r"(tls_base));
32
+ return mem;
33
+ }
34
+
35
+ __attribute__((visibility("default")))
36
+ void* emnapi_async_worker_create() {
37
+ size_t args_size = sizeof(struct worker_args);
38
+ size_t size = args_size;
39
+
40
+ size_t tls_size = __builtin_wasm_tls_size();
41
+
42
+ size_t tls_block_size = 0;
43
+ if (tls_size) {
44
+ size_t tls_align = __builtin_wasm_tls_align();
45
+ tls_block_size = ROUND_UP(tls_size + tls_align - 1, tls_align);
46
+ size += tls_block_size;
47
+ }
48
+
49
+ ptrdiff_t stack_size = 0;
50
+ if (&__stack_high) {
51
+ stack_size = &__stack_high - &__stack_low;
52
+ } else {
53
+ unsigned char *sp;
54
+ __asm__(
55
+ ".globaltype __stack_pointer, i32\n"
56
+ "global.get __stack_pointer\n"
57
+ "local.set %0\n"
58
+ : "=r"(sp));
59
+ stack_size = sp > &__global_base ? &__heap_base - &__data_end : (ptrdiff_t)&__global_base;
60
+ }
61
+
62
+ stack_size = ROUND_UP(stack_size, STACK_ALIGN);
63
+ stack_size = stack_size < DEFAULT_STACK_MAX ? stack_size : DEFAULT_STACK_MAX;
64
+ size += stack_size;
65
+
66
+ void* block = calloc(1, size);
67
+ if (!block) return 0;
68
+
69
+ void* tls_base = tls_size ? __copy_tls(block + args_size) : 0;
70
+
71
+ struct worker_args* args = (struct worker_args*)block;
72
+
73
+ args->stack_base = block + args_size + tls_block_size + stack_size;
74
+ args->tls_base = tls_base;
75
+ return block;
76
+ }
@@ -0,0 +1,33 @@
1
+ #ifdef __wasm64__
2
+ #define PTR i64
3
+ #else
4
+ #define PTR i32
5
+ #endif
6
+
7
+ .text
8
+
9
+ .export_name emnapi_async_worker_init, emnapi_async_worker_init
10
+
11
+ .globaltype __stack_pointer, PTR
12
+ .globaltype __tls_base, PTR
13
+
14
+ .hidden emnapi_async_worker_init
15
+ .globl emnapi_async_worker_init
16
+ .type emnapi_async_worker_init,@function
17
+
18
+ emnapi_async_worker_init:
19
+ .functype emnapi_async_worker_init (PTR) -> ()
20
+
21
+ local.get 0
22
+ PTR.load 0
23
+ global.set __stack_pointer
24
+
25
+ local.get 0
26
+ #ifdef __wasm64__
27
+ PTR.load 8
28
+ #else
29
+ PTR.load 4
30
+ #endif
31
+ global.set __tls_base
32
+
33
+ end_function
@@ -209,6 +209,7 @@ static void post(QUEUE* q, enum uv__work_kind kind) {
209
209
  // nthreads = 0;
210
210
  // }
211
211
 
212
+ EMNAPI_EXTERN int _emnapi_async_work_pool_size();
212
213
 
213
214
  static void init_threads(void) {
214
215
  unsigned int i;
@@ -223,9 +224,14 @@ static void init_threads(void) {
223
224
  nthreads = EMNAPI_WORKER_POOL_SIZE;
224
225
  #else
225
226
  nthreads = ARRAY_SIZE(default_threads);
226
- val = getenv("UV_THREADPOOL_SIZE");
227
- if (val != NULL)
228
- nthreads = atoi(val);
227
+ int async_work_pool_size = _emnapi_async_work_pool_size();
228
+ if (async_work_pool_size > 0) {
229
+ nthreads = (unsigned int) async_work_pool_size;
230
+ } else {
231
+ val = getenv("UV_THREADPOOL_SIZE");
232
+ if (val != NULL)
233
+ nthreads = atoi(val);
234
+ }
229
235
  #endif
230
236
  if (nthreads == 0)
231
237
  nthreads = 1;
@@ -151,9 +151,25 @@ static void uv__async_io(uv_loop_t* loop) {
151
151
  }
152
152
  }
153
153
 
154
+ #if EMNAPI_USE_PROXYING
155
+
156
+ #undef emscripten_main_browser_thread_id
157
+
158
+ __attribute__((weak))
159
+ pthread_t emscripten_main_browser_thread_id(void) {
160
+ return NULL;
161
+ }
162
+
163
+ __attribute__((weak))
164
+ pthread_t emscripten_main_runtime_thread_id(void) {
165
+ return emscripten_main_browser_thread_id();
166
+ }
167
+ #endif
168
+
154
169
  static void uv__async_send(uv_loop_t* loop) {
155
170
  #if EMNAPI_USE_PROXYING
156
- pthread_t main_thread = emscripten_main_browser_thread_id();
171
+ pthread_t main_thread = emscripten_main_runtime_thread_id();
172
+ assert(main_thread != NULL);
157
173
  if (pthread_equal(main_thread, pthread_self())) {
158
174
  NEXT_TICK((void (*)(void *))uv__async_io, loop);
159
175
  } else {