1e41f4b71Sopenharmony_ci# Node-API FAQs 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ci## What should I do when "undefined/not callable" or specific error message is reported for xxx after "import xxx from libxxx.so" is executed? 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci1. Check whether the module name in the .cpp file used in module registration is the same as that in the .so file name. 6e41f4b71Sopenharmony_ci If the module name is **entry**, the .so file name must be **libentry.so**, and the **nm_modname** field in **napi_module** must be **entry**. The module names must be of the same case. 7e41f4b71Sopenharmony_ci 8e41f4b71Sopenharmony_ci2. Check whether the .so file is successfully loaded. 9e41f4b71Sopenharmony_ci 10e41f4b71Sopenharmony_ci Check the log related to module loading during the application startup. Search for the keyword "dlopen" and check for error information. Possible causes include the following: 11e41f4b71Sopenharmony_ci 12e41f4b71Sopenharmony_ci - The file to be loaded does not exist or is in a blocklist. 13e41f4b71Sopenharmony_ci - The application does not have the required permission. 14e41f4b71Sopenharmony_ci - The **nm_modname** value does not match the module name in multi-thread scenarios (such as worker threads and taskpool). The module names must be of the same case. 15e41f4b71Sopenharmony_ci3. Check whether the dependency .so files are successfully loaded. 16e41f4b71Sopenharmony_ci Check that all the dependency .so files are packaged into the application and the application has the permission to open them. 17e41f4b71Sopenharmony_ci 18e41f4b71Sopenharmony_ci4. Check whether the module import mode matches the .so file path. 19e41f4b71Sopenharmony_ci If the module is imported by using **import xxx from '\@ohos.yyy.zzz'**, you may find **libzzz.z.so** or **libzzz_napi.z.so** in **/system/lib/module/yyy** (for a 32-bit system) or **/system/lib64/module/yyy** (for a 64-bit system). If the .so file does not exist or the file names do not match, an error containing the keyword "dlopen" will be reported. 20e41f4b71Sopenharmony_ci 21e41f4b71Sopenharmony_ci 22e41f4b71Sopenharmony_ci 23e41f4b71Sopenharmony_ci| **Error Log**| **Solution**| 24e41f4b71Sopenharmony_ci| -------- | -------- | 25e41f4b71Sopenharmony_ci| module $SO is not allowed to load in restricted runtime | The module, identified by **$SO**, is not allowed for the worker thread running in a restricted environment and cannot be loaded. You are advised to delete the module.| 26e41f4b71Sopenharmony_ci| module $SO is in blocklist, loading prohibited | The module, identified by **$SO**, is in a blocklist due to the control of the widget or Extension, and cannot be loaded. You are advised to delete the module.| 27e41f4b71Sopenharmony_ci| load module failed. $ERRMSG | The dynamic library fails to be loaded. **$ERRMSG** indicates the cause of the loading failure. Possible causes include the following:<br>- The .so file to be loaded does not exist.<br>- The dependency .so file does not exist. <br>- Undefined symbol is found. <br>Locate the cause based on the error message.| 28e41f4b71Sopenharmony_ci| try to load abc file from $FILEPATH failed. | You can load either a dynamic library or an .abc file. If this log information is displayed when you attempt to load a dynamic library, ignore this message. If it is displayed when you attempt to load an .abc file, the .abc file does not exist. **$FILEPATH** indicates the module path.| 29e41f4b71Sopenharmony_ci 30e41f4b71Sopenharmony_ci5. If specific error message is reported, identify the fault based on the error message. 31e41f4b71Sopenharmony_ci 32e41f4b71Sopenharmony_ci| **Error message** | **Fault Analysis & Solution**| 33e41f4b71Sopenharmony_ci| -------- | -------- | 34e41f4b71Sopenharmony_ci| First attempt: $ERRMSG | Loading the .so file with the module name of "xxx" fails. *$ERRMSG* indicates the error information.| 35e41f4b71Sopenharmony_ci| Second attempt: $ERRMSG | Loading the .so file with the module name of "xxx_napi" fails. *$ERRMSG* indicates the error information.| 36e41f4b71Sopenharmony_ci| try to load abc file from xxx failed | Loading the .abc file fails. *xxx* indicates the name of the .abc file.| 37e41f4b71Sopenharmony_ci| module xxx is not allowed to load in restricted runtime. | This module cannot be used in restricted runtime. *xxx* indicates the module name. You are advised to delete the module.| 38e41f4b71Sopenharmony_ci| module xxx is in blocklist, loading prohibited. | The module cannot be used in the current extension. *xxx* indicates the module name. You are advised to delete the module.| 39e41f4b71Sopenharmony_ci 40e41f4b71Sopenharmony_ci## What should I do when an unexpected value is returned by an API and "occur exception need return" is reported? 41e41f4b71Sopenharmony_ci 42e41f4b71Sopenharmony_ciBefore the call is complete, some Node-API interfaces are checked for JavaScript (JS) exceptions in the VM. If an exception is detected, "occur exception need return" will be reported, with the line number of the code and the Node-API interface name. 43e41f4b71Sopenharmony_ci 44e41f4b71Sopenharmony_ciYou can solve this problem as follows: 45e41f4b71Sopenharmony_ci 46e41f4b71Sopenharmony_ci- If the exception does not matter, clear the exception. 47e41f4b71Sopenharmony_ci Call **napi_get_and_clear_last_exception** before "occur exception need return" is printed to clear the exception. 48e41f4b71Sopenharmony_ci 49e41f4b71Sopenharmony_ci- Throw the exception to the ArkTS layer for capture. 50e41f4b71Sopenharmony_ci Throw the exception directly to the ArkTS layer without going through the native logic. 51e41f4b71Sopenharmony_ci 52e41f4b71Sopenharmony_ci## What are the differences between the lifecycle of napi_value and napi_ref? 53e41f4b71Sopenharmony_ci 54e41f4b71Sopenharmony_ci- **native_value** is managed by **HandleScope**. Generally, you do not need to add **HandleScope** for **native_value** (except for **complete callback** of **uv_queue_work**). 55e41f4b71Sopenharmony_ci 56e41f4b71Sopenharmony_ci- **napi_ref** must be deleted manually. 57e41f4b71Sopenharmony_ci 58e41f4b71Sopenharmony_ci## How do I locate the fault if the return value of a Node-API interface is not "napi_ok"? 59e41f4b71Sopenharmony_ci 60e41f4b71Sopenharmony_ciWhen a Node-API interface is successfully executed, **napi_ok** is returned. If the return value is not **napi_ok**, locate the fault as follows: 61e41f4b71Sopenharmony_ci 62e41f4b71Sopenharmony_ci- Check the result of the input parameter null check, which is performed first before a Node-API interface is executed. The code is as follows: 63e41f4b71Sopenharmony_ci 64e41f4b71Sopenharmony_ci ```cpp 65e41f4b71Sopenharmony_ci CHECK_ENV: null check for env. 66e41f4b71Sopenharmony_ci CHECK_ARG: null check for other input parameters. 67e41f4b71Sopenharmony_ci ``` 68e41f4b71Sopenharmony_ci 69e41f4b71Sopenharmony_ci- Check the result of the input parameter type check, which is performed for certain Node-API interfaces. For example, **napi_get_value_double** is used to obtain a C double value from a JS number, and the type of the JS value passed in must be number. The parameter type check is as follows: 70e41f4b71Sopenharmony_ci 71e41f4b71Sopenharmony_ci ```cpp 72e41f4b71Sopenharmony_ci RETURN_STATUS_IF_FALSE(env, nativeValue->TypeOf() == NATIVE_NUMBER, napi_number_expected); 73e41f4b71Sopenharmony_ci ``` 74e41f4b71Sopenharmony_ci 75e41f4b71Sopenharmony_ci- Check the return value, which contains the verification result of certain interfaces. For example, **napi_call_function** is used to execute a JS function. If an exception occurs in the JS function, Node-API returns **napi_pending_exception**. 76e41f4b71Sopenharmony_ci 77e41f4b71Sopenharmony_ci ```cpp 78e41f4b71Sopenharmony_ci auto resultValue = engine->CallFunction(nativeRecv, nativeFunc, nativeArgv, argc); 79e41f4b71Sopenharmony_ci RETURN_STATUS_IF_FALSE(env, resultValue != nullptr, napi_pending_exception) 80e41f4b71Sopenharmony_ci ``` 81e41f4b71Sopenharmony_ci 82e41f4b71Sopenharmony_ci- Determine the status value returned, and analyze the situation in which the status value is returned. 83e41f4b71Sopenharmony_ci 84e41f4b71Sopenharmony_ci## What should I do if memory leaks when napi_threadsafe_function is used? 85e41f4b71Sopenharmony_ci 86e41f4b71Sopenharmony_ciWhen **napi_threadsafe_function** (**tsfn** for short) is used, **napi_acquire_threadsafe_function** is often called to change the reference count of **tsfn** to ensure that **tsfn** is not released unexpectedly. When all the **tsfn** calls are complete, **napi_release_threadsafe_function** should be called in **napi_tsfn_release** mode in a timely manner to ensure that the reference count returns to the value before **napi_acquire_threadsafe_function** is called. **tsfn** can be correctly released only when the reference count is **0**. 87e41f4b71Sopenharmony_ci 88e41f4b71Sopenharmony_ciWhen **env** is about to exit but the reference count of **tsfn** is not **0**, **napi_release_threadsafe_function** should be called in **napi_tsfn_abort** mode to ensure that **tsfn** is not held or used by **env** after **env** is released. If **env** continues to hold and use **tsfn** after exiting, the application may crash. 89e41f4b71Sopenharmony_ci 90e41f4b71Sopenharmony_ciThe following code shows how to register **env_cleanup** to ensure that **tsfn** is no longer held by **env** after **env** exits. 91e41f4b71Sopenharmony_ci 92e41f4b71Sopenharmony_ci```cpp 93e41f4b71Sopenharmony_ci#include <hilog/log.h> // To output logs, link libhilog_ndk.z.so. 94e41f4b71Sopenharmony_ci#include <thread> // Include the thread module to create and mange threads. 95e41f4b71Sopenharmony_ci#include <unistd.h> // Include unistd.h to suspend the execution of the calling thread. 96e41f4b71Sopenharmony_ci 97e41f4b71Sopenharmony_ci// Define the log domain and tag. 98e41f4b71Sopenharmony_ci#undef LOG_DOMAIN 99e41f4b71Sopenharmony_ci#undef LOG_TAG 100e41f4b71Sopenharmony_ci#define LOG_DOMAIN 0x2342 101e41f4b71Sopenharmony_ci#define LOG_TAG "MY_TSFN_DEMO" 102e41f4b71Sopenharmony_ci 103e41f4b71Sopenharmony_ci/* 104e41f4b71Sopenharmony_ci To construct a scenario in which the env lifecycle is shorter than the native lifecycle, 105e41f4b71Sopenharmony_ci the following uses worker, taskpool, and napi_create_ark_runtime to create an ArkTS 106e41f4b71Sopenharmony_ci running environment for a worker thread and manually stop the thread in advance. 107e41f4b71Sopenharmony_ci*/ 108e41f4b71Sopenharmony_ci 109e41f4b71Sopenharmony_ci 110e41f4b71Sopenharmony_ci// Define a struct to simulate the scenario where tsfn is stored. 111e41f4b71Sopenharmony_ciclass MyTsfnContext { 112e41f4b71Sopenharmony_cipublic: 113e41f4b71Sopenharmony_ci// MyTsfnContext is constructed only in a JS thread because Node-API is used. 114e41f4b71Sopenharmony_ciMyTsfnContext(napi_env env, napi_value workName) { 115e41f4b71Sopenharmony_ci // Register the env_cleanup_hook function. 116e41f4b71Sopenharmony_ci napi_add_env_cleanup_hook(env, Cleanup, this); 117e41f4b71Sopenharmony_ci // Create a thread-safe function. 118e41f4b71Sopenharmony_ci if (napi_create_threadsafe_function(env, nullptr, nullptr, workName, 1, 1, this, 119e41f4b71Sopenharmony_ci TsfnFinalize, this, TsfnCallJs, &tsfn_) != napi_ok) { 120e41f4b71Sopenharmony_ci OH_LOG_INFO(LOG_APP, "tsfn is created faild"); 121e41f4b71Sopenharmony_ci return; 122e41f4b71Sopenharmony_ci }; 123e41f4b71Sopenharmony_ci}; 124e41f4b71Sopenharmony_ci 125e41f4b71Sopenharmony_ci~MyTsfnContext() { OH_LOG_INFO(LOG_APP, "MyTsfnContext is deconstructed"); }; 126e41f4b71Sopenharmony_ci 127e41f4b71Sopenharmony_cinapi_threadsafe_function GetTsfn() { 128e41f4b71Sopenharmony_ci std::unique_lock<std::mutex> lock(mutex_); 129e41f4b71Sopenharmony_ci return tsfn_; 130e41f4b71Sopenharmony_ci} 131e41f4b71Sopenharmony_ci 132e41f4b71Sopenharmony_cibool Acquire() { 133e41f4b71Sopenharmony_ci if (GetTsfn() == nullptr) { 134e41f4b71Sopenharmony_ci return false; 135e41f4b71Sopenharmony_ci }; 136e41f4b71Sopenharmony_ci return (napi_acquire_threadsafe_function(GetTsfn()) == napi_ok); 137e41f4b71Sopenharmony_ci}; 138e41f4b71Sopenharmony_ci 139e41f4b71Sopenharmony_cibool Release() { 140e41f4b71Sopenharmony_ci if (GetTsfn() == nullptr) { 141e41f4b71Sopenharmony_ci return false; 142e41f4b71Sopenharmony_ci }; 143e41f4b71Sopenharmony_ci return (napi_release_threadsafe_function(GetTsfn(), napi_tsfn_release) == napi_ok); 144e41f4b71Sopenharmony_ci}; 145e41f4b71Sopenharmony_ci 146e41f4b71Sopenharmony_cibool Call(void *data) { 147e41f4b71Sopenharmony_ci if (GetTsfn() == nullptr) { 148e41f4b71Sopenharmony_ci return false; 149e41f4b71Sopenharmony_ci }; 150e41f4b71Sopenharmony_ci return (napi_call_threadsafe_function(GetTsfn(), data, napi_tsfn_blocking) == napi_ok); 151e41f4b71Sopenharmony_ci}; 152e41f4b71Sopenharmony_ci 153e41f4b71Sopenharmony_ciprivate: 154e41f4b71Sopenharmony_ci// Ensure correct read and write of tsfn by multiple threads. 155e41f4b71Sopenharmony_cistd::mutex mutex_; 156e41f4b71Sopenharmony_cinapi_threadsafe_function tsfn_ = nullptr; 157e41f4b71Sopenharmony_ci 158e41f4b71Sopenharmony_ci// Call napi_add_env_cleanup_hook. 159e41f4b71Sopenharmony_cistatic void Cleanup(void *data) { 160e41f4b71Sopenharmony_ci MyTsfnContext *that = reinterpret_cast<MyTsfnContext *>(data); 161e41f4b71Sopenharmony_ci napi_threadsafe_function tsfn = that->GetTsfn(); 162e41f4b71Sopenharmony_ci std::unique_lock<std::mutex> lock(that->mutex_); 163e41f4b71Sopenharmony_ci that->tsfn_ = nullptr; 164e41f4b71Sopenharmony_ci lock.unlock(); 165e41f4b71Sopenharmony_ci OH_LOG_WARN(LOG_APP, "cleanup is called"); 166e41f4b71Sopenharmony_ci napi_release_threadsafe_function(tsfn, napi_tsfn_abort); 167e41f4b71Sopenharmony_ci}; 168e41f4b71Sopenharmony_ci 169e41f4b71Sopenharmony_ci// Callback to be invoked when tsfn is released. 170e41f4b71Sopenharmony_cistatic void TsfnFinalize(napi_env env, void *data, void *hint) { 171e41f4b71Sopenharmony_ci MyTsfnContext *ctx = reinterpret_cast<MyTsfnContext *>(data); 172e41f4b71Sopenharmony_ci OH_LOG_INFO(LOG_APP, "tsfn is released"); 173e41f4b71Sopenharmony_ci napi_remove_env_cleanup_hook(env, MyTsfnContext::Cleanup, ctx); 174e41f4b71Sopenharmony_ci // Cleanup releases the thread-safe function in advance. To avoid UAF, enable the caller to trigger the release. 175e41f4b71Sopenharmony_ci if (ctx->GetTsfn() != nullptr) { 176e41f4b71Sopenharmony_ci OH_LOG_INFO(LOG_APP, "ctx is released"); 177e41f4b71Sopenharmony_ci delete ctx; 178e41f4b71Sopenharmony_ci } 179e41f4b71Sopenharmony_ci}; 180e41f4b71Sopenharmony_ci 181e41f4b71Sopenharmony_ci// Callback sent by tsfn to the JS thread for execution. 182e41f4b71Sopenharmony_cistatic void TsfnCallJs(napi_env env, napi_value func, void *context, void *data) { 183e41f4b71Sopenharmony_ci MyTsfnContext *ctx = reinterpret_cast<MyTsfnContext *>(context); 184e41f4b71Sopenharmony_ci char *str = reinterpret_cast<char *>(data); 185e41f4b71Sopenharmony_ci OH_LOG_INFO(LOG_APP, "tsfn is called, data is: \"%{public}s\"", str); 186e41f4b71Sopenharmony_ci // The service logic is omitted here. 187e41f4b71Sopenharmony_ci}; 188e41f4b71Sopenharmony_ci}; 189e41f4b71Sopenharmony_ci 190e41f4b71Sopenharmony_ci// Register the myTsfnDemo method with the module. The myTsfnDemo method is defined as follows: 191e41f4b71Sopenharmony_ci// export const myTsfnDemo: () => void; 192e41f4b71Sopenharmony_cinapi_value MyTsfnDemo(napi_env env, napi_callback_info info) { 193e41f4b71Sopenharmony_ci OH_LOG_ERROR(LOG_APP, "MyTsfnDemo is called"); 194e41f4b71Sopenharmony_ci napi_value workName = nullptr; 195e41f4b71Sopenharmony_ci napi_create_string_utf8(env, "MyTsfnWork", NAPI_AUTO_LENGTH, &workName); 196e41f4b71Sopenharmony_ci MyTsfnContext *myContext = new MyTsfnContext(env, workName); 197e41f4b71Sopenharmony_ci if (myContext->GetTsfn() == nullptr) { 198e41f4b71Sopenharmony_ci OH_LOG_ERROR(LOG_APP, "faild to create tsfn"); 199e41f4b71Sopenharmony_ci delete myContext; 200e41f4b71Sopenharmony_ci return nullptr; 201e41f4b71Sopenharmony_ci }; 202e41f4b71Sopenharmony_ci char *data0 = "Im call in ArkTS Thread"; 203e41f4b71Sopenharmony_ci if (!myContext->Call(data0)) { 204e41f4b71Sopenharmony_ci OH_LOG_INFO(LOG_APP, "call tsfn failed"); 205e41f4b71Sopenharmony_ci }; 206e41f4b71Sopenharmony_ci 207e41f4b71Sopenharmony_ci // Create a thread to simulate an asynchronous operation. 208e41f4b71Sopenharmony_ci std::thread( 209e41f4b71Sopenharmony_ci [](MyTsfnContext *myCtx) { 210e41f4b71Sopenharmony_ci if (!myCtx->Acquire()) { 211e41f4b71Sopenharmony_ci OH_LOG_ERROR(LOG_APP, "acquire tsfn faild"); 212e41f4b71Sopenharmony_ci return; 213e41f4b71Sopenharmony_ci }; 214e41f4b71Sopenharmony_ci char *data1 = "Im call in std::thread"; 215e41f4b71Sopenharmony_ci // This operation is optional and used only to check whether the asynchronous tsfn is still valid. 216e41f4b71Sopenharmony_ci if (!myCtx->Call(data1)) { 217e41f4b71Sopenharmony_ci OH_LOG_ERROR(LOG_APP, "call tsfn failed"); 218e41f4b71Sopenharmony_ci }; 219e41f4b71Sopenharmony_ci // Suspend the thread for 5 seconds to simulate a time-consuming operation, which is not complete when env exits. 220e41f4b71Sopenharmony_ci sleep(5); 221e41f4b71Sopenharmony_ci // When the asynchronous operation is complete, tsfn has been released and set to nullptr. 222e41f4b71Sopenharmony_ci char *data2 = "Im call after work"; 223e41f4b71Sopenharmony_ci if (!myCtx->Call(data2) && !myCtx->Release()) { 224e41f4b71Sopenharmony_ci OH_LOG_ERROR(LOG_APP, "call and release tsfn failed"); 225e41f4b71Sopenharmony_ci delete myCtx; 226e41f4b71Sopenharmony_ci } 227e41f4b71Sopenharmony_ci }, myContext).detach(); 228e41f4b71Sopenharmony_ci return nullptr; 229e41f4b71Sopenharmony_ci}; 230e41f4b71Sopenharmony_ci 231e41f4b71Sopenharmony_ci``` 232e41f4b71Sopenharmony_ci 233e41f4b71Sopenharmony_ciThe following is the main thread logic, which creates worker threads and instruct workers to execute tasks. 234e41f4b71Sopenharmony_ci 235e41f4b71Sopenharmony_ci```ts 236e41f4b71Sopenharmony_ci// Main thread. 237e41f4b71Sopenharmony_ciimport worker, { MessageEvents } from '@ohos.worker'; 238e41f4b71Sopenharmony_ci 239e41f4b71Sopenharmony_ciconst mWorker = new worker.ThreadWorker('../workers/Worker'); 240e41f4b71Sopenharmony_cimWorker.onmessage = (e: MessageEvents) => { 241e41f4b71Sopenharmony_ci const action: string | undefined = e.data?.action; 242e41f4b71Sopenharmony_ci if (action === 'kill') { 243e41f4b71Sopenharmony_ci mWorker.terminate(); 244e41f4b71Sopenharmony_ci } 245e41f4b71Sopenharmony_ci} 246e41f4b71Sopenharmony_ci 247e41f4b71Sopenharmony_ci// The registration of the triggering mode is omitted. 248e41f4b71Sopenharmony_cimWorker.postMessage({action: 'tsfn-demo'}) 249e41f4b71Sopenharmony_ci 250e41f4b71Sopenharmony_ci``` 251e41f4b71Sopenharmony_ci 252e41f4b71Sopenharmony_ciThe following is the worker thread logic, which triggers native tasks. 253e41f4b71Sopenharmony_ci 254e41f4b71Sopenharmony_ci```ts 255e41f4b71Sopenharmony_ci// worker.ets 256e41f4b71Sopenharmony_ciimport worker, { ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker'; 257e41f4b71Sopenharmony_ciimport napiModule from 'libentry.so'; // libentry.so is the module name of the Node-API library. 258e41f4b71Sopenharmony_ci 259e41f4b71Sopenharmony_ciconst workerPort: ThreadWorkerGlobalScope = worker.workerPort; 260e41f4b71Sopenharmony_ci 261e41f4b71Sopenharmony_ciworkerPort.onmessage = (e: MessageEvents) => { 262e41f4b71Sopenharmony_ci const action: string | undefined = e.data?.action; 263e41f4b71Sopenharmony_ci if (action === 'tsfn-demo') { 264e41f4b71Sopenharmony_ci // Trigger the tsfn demo in C++. 265e41f4b71Sopenharmony_ci napiModule.myTsfnDemo(); 266e41f4b71Sopenharmony_ci // Instruct the main thread to terminate the worker. 267e41f4b71Sopenharmony_ci workerPort.postMessage({action: 'kill'}); 268e41f4b71Sopenharmony_ci }; 269e41f4b71Sopenharmony_ci} 270e41f4b71Sopenharmony_ci``` 271