1# Thread Safety Development Using Node-API 2 3 4## When to Use 5 6**napi_create_threadsafe_function** is a Node-API interface used to create a thread-safe JS function, which can be called from multiple threads without race conditions or deadlocks. Thread-safe functions can be used in the following scenarios: 7 8 9- Asynchronous computing: If a time-consuming computing or I/O operation needs to be performed, you can create a thread-safe function to have the computing or I/O operation executed in a dedicated thread. This ensures normal running of the main thread and improves the response speed of your application. 10 11- Data sharing: When multiple threads need to access the same data, using a thread-safe function can prevent race conditions or deadlocks during data read and write operations. 12 13- Multithread programming: In the case of multithread programming, a thread-safe function can ensure communication and synchronization between multiple threads. 14 15 16## Example 17 181. Define a thread-safe function at the native entry. 19 ```c++ 20 #include "napi/native_api.h" 21 #include "hilog/log.h" 22 #include <future> 23 24 struct CallbackData { 25 napi_threadsafe_function tsfn; 26 napi_async_work work; 27 }; 28 29 static napi_value StartThread(napi_env env, napi_callback_info info) 30 { 31 size_t argc = 1; 32 napi_value jsCb = nullptr; 33 CallbackData *callbackData = nullptr; 34 napi_get_cb_info(env, info, &argc, &jsCb, nullptr, reinterpret_cast<void **>(&callbackData)); 35 36 // Create a thread-safe function. 37 napi_value resourceName = nullptr; 38 napi_create_string_utf8(env, "Thread-safe Function Demo", NAPI_AUTO_LENGTH, &resourceName); 39 napi_create_threadsafe_function(env, jsCb, nullptr, resourceName, 0, 1, callbackData, nullptr, 40 callbackData, CallJs, &callbackData->tsfn); 41 42 // Create an asynchronous work object. 43 // ExecuteWork is executed on a non-JS thread created by libuv. The napi_create_async_work is used to simulate the scenario, in which napi_call_threadsafe_function is used to submit tasks to a JS thread from a non-JS thread. 44 napi_create_async_work(env, nullptr, resourceName, ExecuteWork, WorkComplete, callbackData, 45 &callbackData->work); 46 47 // Add the asynchronous work object to the asynchronous task queue. 48 napi_queue_async_work(env, callbackData->work); 49 return nullptr; 50 } 51 ``` 52 532. Call **ExecuteWork** in a worker thread to execute the thread-safe function. 54 ```c++ 55 static void ExecuteWork(napi_env env, void *data) 56 { 57 CallbackData *callbackData = reinterpret_cast<CallbackData *>(data); 58 std::promise<std::string> promise; 59 auto future = promise.get_future(); 60 napi_call_threadsafe_function(callbackData->tsfn, &promise, napi_tsfn_nonblocking); 61 try { 62 auto result = future.get(); 63 // OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", result.c_str()); 64 } catch (const std::exception &e) { 65 // OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", e.what()); 66 } 67 } 68 ``` 69 703. Execute the asynchronous callback in a JS thread. 71 ```c++ 72 static napi_value ResolvedCallback(napi_env env, napi_callback_info info) 73 { 74 void *data = nullptr; 75 size_t argc = 1; 76 napi_value argv[1]; 77 if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) { 78 return nullptr; 79 } 80 size_t result = 0; 81 char buf[32] = {0}; 82 napi_get_value_string_utf8(env, argv[0], buf, 32, &result); 83 reinterpret_cast<std::promise<std::string> *>(data)->set_value(std::string(buf)); 84 return nullptr; 85 } 86 87 static napi_value RejectedCallback(napi_env env, napi_callback_info info) 88 { 89 void *data = nullptr; 90 if (napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data) != napi_ok) { 91 return nullptr; 92 } 93 reinterpret_cast<std::promise<std::string> *>(data)->set_exception( 94 std::make_exception_ptr(std::runtime_error("Error in jsCallback"))); 95 return nullptr; 96 } 97 98 static void CallJs(napi_env env, napi_value jsCb, void *context, void *data) 99 { 100 if (env == nullptr) { 101 return; 102 } 103 napi_value undefined = nullptr; 104 napi_value promise = nullptr; 105 napi_get_undefined(env, &undefined); 106 napi_call_function(env, undefined, jsCb, 0, nullptr, &promise); 107 napi_value thenFunc = nullptr; 108 if (napi_get_named_property(env, promise, "then", &thenFunc) != napi_ok) { 109 return; 110 } 111 napi_value resolvedCallback; 112 napi_value rejectedCallback; 113 napi_create_function(env, "resolvedCallback", NAPI_AUTO_LENGTH, ResolvedCallback, data, 114 &resolvedCallback); 115 napi_create_function(env, "rejectedCallback", NAPI_AUTO_LENGTH, RejectedCallback, data, 116 &rejectedCallback); 117 napi_value argv[2] = {resolvedCallback, rejectedCallback}; 118 napi_call_function(env, promise, thenFunc, 2, argv, nullptr); 119 } 120 ``` 121 1224. After the task is complete, clear and reclaim resources. 123 ```c++ 124 static void WorkComplete(napi_env env, napi_status status, void *data) 125 { 126 CallbackData *callbackData = reinterpret_cast<CallbackData *>(data); 127 napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release); 128 napi_delete_async_work(env, callbackData->work); 129 callbackData->tsfn = nullptr; 130 callbackData->work = nullptr; 131 } 132 ``` 133 1345. Initialize the module and call the API from ArkTS. 135 ```c++ 136 // Initialize the module. 137 static napi_value Init(napi_env env, napi_value exports) { 138 CallbackData *callbackData = new CallbackData(); // Release when the thread exits. 139 napi_property_descriptor desc[] = { 140 {"startThread", nullptr, StartThread, nullptr, nullptr, nullptr, napi_default, callbackData}, 141 }; 142 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 143 return exports; 144 } 145 ``` 146 147 ``` ts 148 // Description of the interface in the .d.ts file. 149 export const startThread: (callback: () => Promise<string>) => void; 150 151 // Call the API of ArkTS. 152 import nativeModule from 'libentry.so'; // Import native capabilities. 153 154 let callback = (): Promise<string> => { 155 return new Promise((resolve) => { 156 setTimeout(() => { 157 resolve("string from promise"); 158 }, 5000); 159 }); 160 } 161 nativeModule.startThread(callback); 162 ``` 163