• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 constexpr int INT_NUM_2 = 2;   // Integer 2
73   static constexpr int INT_BUF_32 = 32; // The length of the integer string is 32.
74
75   static napi_value ResolvedCallback(napi_env env, napi_callback_info info)
76   {
77       void *data = nullptr;
78       size_t argc = 1;
79       napi_value argv[1];
80       if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) {
81           return nullptr;
82       }
83       size_t result = 0;
84       char buf[32] = {0};
85       napi_get_value_string_utf8(env, argv[0], buf, INT_BUF_32, &result);
86       reinterpret_cast<std::promise<std::string> *>(data)->set_value(std::string(buf));
87       return nullptr;
88   }
89
90   static napi_value RejectedCallback(napi_env env, napi_callback_info info)
91   {
92       void *data = nullptr;
93       if (napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data) != napi_ok) {
94           return nullptr;
95       }
96       reinterpret_cast<std::promise<std::string> *>(data)->set_exception(
97           std::make_exception_ptr(std::runtime_error("Error in jsCallback")));
98       return nullptr;
99   }
100
101   static void CallJs(napi_env env, napi_value jsCb, void *context, void *data)
102   {
103       if (env == nullptr) {
104           return;
105       }
106       napi_value undefined = nullptr;
107       napi_value promise = nullptr;
108       napi_get_undefined(env, &undefined);
109       napi_call_function(env, undefined, jsCb, 0, nullptr, &promise);
110       napi_value thenFunc = nullptr;
111       if (napi_get_named_property(env, promise, "then", &thenFunc) != napi_ok) {
112           return;
113       }
114       napi_value resolvedCallback;
115       napi_value rejectedCallback;
116       napi_create_function(env, "resolvedCallback", NAPI_AUTO_LENGTH, ResolvedCallback, data,
117   					     &resolvedCallback);
118       napi_create_function(env, "rejectedCallback", NAPI_AUTO_LENGTH, RejectedCallback, data,
119   					     &rejectedCallback);
120       napi_value argv[2] = {resolvedCallback, rejectedCallback};
121       napi_call_function(env, promise, thenFunc, INT_NUM_2, argv, nullptr);
122   }
123   ```
124
1254. After the task is complete, clear and reclaim resources.
126   ```c++
127   static void WorkComplete(napi_env env, napi_status status, void *data)
128   {
129       CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
130       napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release);
131       napi_delete_async_work(env, callbackData->work);
132       callbackData->tsfn = nullptr;
133       callbackData->work = nullptr;
134   }
135   ```
136
1375. Initialize the module and call the API from ArkTS.
138   ```c++
139   // Initialize the module.
140   static napi_value Init(napi_env env, napi_value exports) {
141       CallbackData *callbackData = new CallbackData(); // Release when the thread exits.
142       napi_property_descriptor desc[] = {
143           {"startThread", nullptr, StartThread, nullptr, nullptr, nullptr, napi_default, callbackData},
144       };
145       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
146       return exports;
147   }
148   ```
149
150   ``` ts
151   // Description of the interface in the .d.ts file.
152    export const startThread: (callback: () => Promise<string>) => void;
153
154   // Call the API of ArkTS.
155   import nativeModule from 'libentry.so'; // Import native capabilities.
156
157   let callback = (): Promise<string> => {
158     return new Promise((resolve) => {
159       setTimeout(() => {
160           resolve("string from promise");
161         }, 5000);
162       });
163    }
164    nativeModule.startThread(callback);
165   ```
166