emnapi 0.33.1 → 0.35.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:
@@ -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
  }
@@ -732,20 +725,60 @@ pub unsafe extern "C" fn napi_register_wasm_v1(env: napi_env, exports: napi_valu
732
725
 
733
726
  ### Multithread
734
727
 
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.
728
+ Related API:
729
+
730
+ - [napi_*_async_work](https://nodejs.org/dist/latest/docs/api/n-api.html#napi_create_async_work)
731
+ - [napi_*_threadsafe_function](https://nodejs.org/dist/latest/docs/api/n-api.html#asynchronous-thread-safe-function-calls)
732
+
733
+ They are available in emnapi, but you need to know more details before you start to use them.
734
+ Now emnapi has 3 implementations of async work and 2 implementations of TSFN:
735
+
736
+ - Async work
737
+ - A. Libuv threadpool and pthread based implementation in C
738
+ - B. Single thread mock in JavaScript
739
+ - C. Web worker based implementation in C (stack allocation) and JavaScript
740
+ - TSFN
741
+ - D. Libuv and pthread based implementation in C
742
+ - E. Web worker based implementation in JavaScript
743
+
744
+ | | Library to Link | `wasm32-emscripten` | `wasm32` | `wasm32-wasi` | `wasm32-wasi-threads` |
745
+ |---|------------------------|---------------------|----------|---------------|-----------------------|
746
+ | A | libemnapi-mt.a | ✅ | ❌ | ❌ | ✅ |
747
+ | B | libemnapi-basic(-mt).a | ✅ | ✅ | ✅ | ✅ |
748
+ | C | libemnapi-basic-mt.a | ❌ | ✅ | ❌ | ✅ |
749
+ | D | libemnapi-mt.a | ✅ | ❌ | ❌ | ✅ |
750
+ | E | libemnapi-basic-mt.a | ✅ | ✅ | ✅ | ✅ |
738
751
 
739
- **This is EXPERIMENTAL on non-emscripten.**
752
+ There are some limitations on browser about wasi-libc's pthread implementation, for example
753
+ `pthread_mutex_lock` may call `__builtin_wasm_memory_atomic_wait32`(`memory.atomic.wait32`)
754
+ which is disallowed in browser JS main thread. While Emscripten's pthread implementation
755
+ has considered usage in browser. If you need to run your addon with multithreaded features on browser,
756
+ we recommend you use Emscripten A & D, or bare wasm32 C & E.
757
+
758
+ #### About Prebuilt Libraries
759
+
760
+ Prebuilt libraries can be found in the `lib` directory in `emnapi` npm package.
761
+
762
+ | Library | Description | `wasm32-emscripten` | `wasm32` | `wasm32-wasi` | `wasm32-wasi-threads` |
763
+ |----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------|----------|---------------|-----------------------------------------|
764
+ | 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 |
765
+ | 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 |
766
+ | 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 |
767
+ | 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 |
768
+ | libdlmalloc.a | no atomics feature, no thread safe garanteed. | ❌ | ✅ | ❌ | ❌ |
769
+ | libdlmalloc-mt.a | atomics feature enabled, thread safe. | ❌ | ✅ | ❌ | ❌ |
770
+ | libemmalloc.a | no atomics feature, no thread safe garanteed. | ❌ | ✅ | ❌ | ❌ |
771
+ | libemmalloc-mt.a | atomics feature enabled, thread safe. | ❌ | ✅ | ❌ | ❌ |
772
+
773
+ #### Usage
740
774
 
741
775
  ```cmake
742
776
  add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/node_modules/emnapi")
743
777
 
744
778
  add_executable(hello hello.c)
745
779
 
746
- target_link_libraries(hello emnapi-mt)
747
-
748
780
  if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
781
+ target_link_libraries(hello emnapi-mt)
749
782
  target_compile_options(hello PRIVATE "-pthread")
750
783
  target_link_options(hello PRIVATE
751
784
  "-sALLOW_MEMORY_GROWTH=1"
@@ -758,6 +791,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
758
791
  )
759
792
  elseif(CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi-threads")
760
793
  # Experimental
794
+ target_link_libraries(hello emnapi-mt)
761
795
  set_target_properties(hello PROPERTIES SUFFIX ".wasm")
762
796
  target_compile_options(hello PRIVATE "-fno-exceptions" "-pthread")
763
797
  target_link_options(hello PRIVATE
@@ -771,6 +805,19 @@ elseif(CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi-threads")
771
805
  "-Wl,--import-undefined"
772
806
  "-Wl,--export-table"
773
807
  )
808
+ elseif((CMAKE_C_COMPILER_TARGET STREQUAL "wasm32") OR (CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-unknown-unknown"))
809
+ target_link_libraries(hello emnapi-basic-mt)
810
+ set_target_properties(hello PROPERTIES SUFFIX ".wasm")
811
+ target_compile_options(hello PRIVATE "-fno-exceptions" "-matomics" "-mbulk-memory")
812
+ target_link_options(hello PRIVATE
813
+ "-nostdlib"
814
+ "-Wl,--no-entry"
815
+ "-Wl,--export=napi_register_wasm_v1"
816
+ "-Wl,--export=emnapi_async_worker_create"
817
+ "-Wl,--export=emnapi_async_worker_init"
818
+ "-Wl,--import-memory,--shared-memory,--max-memory=2147483648,--import-undefined"
819
+ "-Wl,--export-dynamic,--export=malloc,--export=free,--export-table"
820
+ )
774
821
  endif()
775
822
  ```
776
823
 
@@ -784,6 +831,11 @@ cmake -DCMAKE_TOOLCHAIN_FILE=$WASI_SDK_PATH/share/cmake/wasi-sdk-pthread.cmake \
784
831
  -DCMAKE_BUILD_TYPE=Release \
785
832
  -G Ninja -H. -Bbuild
786
833
 
834
+ cmake -DCMAKE_TOOLCHAIN_FILE=node_modules/emnapi/cmake/wasm32.cmake \
835
+ -DWASI_SDK_PREFIX=$WASI_SDK_PATH \
836
+ -DCMAKE_BUILD_TYPE=Release \
837
+ -G Ninja -H. -Bbuild
838
+
787
839
  cmake --build build
788
840
  ```
789
841
 
@@ -793,6 +845,15 @@ And additional work is required during instantiating wasm compiled with non-emsc
793
845
  // emnapi main thread (could be in a Worker)
794
846
  instantiateNapiModule(input, {
795
847
  context: getDefaultContext(),
848
+ /**
849
+ * emscripten
850
+ * 0: no effect
851
+ * > 0: the same effect to UV_THREADPOOL_SIZE
852
+ * non-emscripten
853
+ * 0: single thread mock
854
+ * > 0 schedule async work in web worker
855
+ */
856
+ asyncWorkPoolSize: 4, // 0: single thread mock, > 0: schedule async work in web worker
796
857
  wasi: new WASI(/* ... */),
797
858
  // reuseWorker: true,
798
859
  onCreateWorker () {
@@ -890,17 +951,30 @@ instantiateNapiModule(input, {
890
951
 
891
952
  ### `-DEMNAPI_WORKER_POOL_SIZE=4`
892
953
 
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:
954
+ 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
955
 
895
956
  ```js
957
+ Module.init({
958
+ // ...
959
+ asyncWorkPoolSize: 2
960
+ })
961
+
962
+ // if asyncWorkPoolSize is not specified
896
963
  Module.preRun = Module.preRun || [];
897
964
  Module.preRun.push(function () {
898
965
  if (typeof ENV !== 'undefined') {
899
966
  ENV.UV_THREADPOOL_SIZE = '2';
900
967
  }
901
968
  });
969
+ ```
902
970
 
971
+ ```js
903
972
  // wasi
973
+ instantiateNapiModule({
974
+ // ...
975
+ asyncWorkPoolSize: 2
976
+ })
977
+ // if asyncWorkPoolSize is not specified
904
978
  new WASI({
905
979
  env: {
906
980
  UV_THREADPOOL_SIZE: '2'
@@ -908,7 +982,7 @@ new WASI({
908
982
  })
909
983
  ```
910
984
 
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.
985
+ It represent max of `EMNAPI_WORKER_POOL_SIZE` async work (`napi_queue_async_work`) can be executed in parallel. Default is not defined.
912
986
 
913
987
  You can set both `PTHREAD_POOL_SIZE` and `EMNAPI_WORKER_POOL_SIZE` to `number of CPU cores` in general.
914
988
  If you use another library function which may create `N` child threads in async work,