• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# C++ Inter-Thread Data Sharing
2
3When an application performs multithreaded computations at the C++ layer, ArkTS APIs needs to be executed within the ArkTS environment. To prevent the non-UI main thread from waiting for the API call results within the UI main thread, you need to create an ArkTS execution environment on these C++ threads and directly call the APIs. In addition, you may need to share and manipulate Sendable objects across C++ threads.
4
5To support this scenario, you must create the capabilities to call ArkTS APIs on C++ threads, and share and manipulate Sendable objects across threads.
6
7
8## Calling ArkTS APIs on C++ Threads
9
10For details about how to use Node-API to create an ArkTS runtime environment and call ArkTS APIs on C++ threads, see [Creating an ArkTS Runtime Environment Using Node-API](../napi/use-napi-ark-runtime.md).
11
12The core code snippet is as follows:
13
14ArkTS file definition
15
16```ts
17// SendableObjTest.ets
18@Sendable
19export class SendableObjTest {
20  static newSendable() {
21    return 1024;
22  }
23}
24```
25<!-- @[arkts_define_obj](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTs/ArkTsConcurrent/ApplicationMultithreadingDevelopment/NativeInterthreadShared/entry/src/main/ets/pages/SendableObjTest.ets) -->
26
27
28Native implementation to load ArkTS modules
29
30```cpp
31// napi_init.cpp
32#include "napi/native_api.h"
33#include <thread>
34static void *CreateArkRuntimeFunc(void *arg)
35{
36    // 1. Create the ArkTS runtime environment.
37    napi_env env = nullptr;
38    napi_status ret = napi_create_ark_runtime(&env);
39    if (ret != napi_ok) {
40        std::abort();
41    }
42    // 2. Load the custom module, assuming that SendableObjTest provides the newSendable function for creating Sendable objects.
43    napi_value test = nullptr;
44    ret = napi_load_module_with_info(env, "entry/src/main/ets/pages/SendableObjTest", "com.example.myapplication/entry", &test);
45    if (ret != napi_ok) {
46        std::abort();
47    }
48    napi_value sendableObjTest = nullptr;
49    ret = napi_get_named_property(env, test, "SendableObjTest", &sendableObjTest);
50    if (ret != napi_ok) {
51        std::abort();
52    }
53    // 3. Use newSendable in ArkTS, assuming that the newSendable function in sendableObjTest returns a Sendable object.
54    napi_value newSendable = nullptr;
55    ret = napi_get_named_property(env, sendableObjTest, "newSendable", &newSendable);
56    if (ret != napi_ok) {
57        std::abort();
58    }
59    // 4. Call the newSendable function to return the newly created Sendable object and store it in result.
60    napi_value result = nullptr;
61    ret = napi_call_function(env, sendableObjTest, newSendable, 0, nullptr, &result);
62    if (ret != napi_ok) {
63        std::abort();
64    }
65    // 5. Obtain the result returned by ArkTS.
66    int value0;
67    napi_get_value_int32(env, result, &value0);
68    if (value0 != 1024) {
69        std::abort();
70    }
71    // 6. Destroy the ArkTS runtime environment.
72    ret = napi_destroy_ark_runtime(&env);
73    return nullptr;
74}
75```
76<!-- @[native_load_arkts_module](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTs/ArkTsConcurrent/ApplicationMultithreadingDevelopment/NativeInterthreadShared/entry/src/main/cpp/napi_init.cpp) -->
77
78The process consists of four steps: creating an execution environment, loading modules, searching for and calling functions (or directly creating Sendable objects through Node-API), and finally destroying the execution environment. For details about how to load modules, see [Loading a Module Using Node-API](../napi/use-napi-load-module-with-info.md). For details about how to search for and call functions and more Node-API capabilities, see [Node-API](../reference/native-lib/napi.md#node-api).
79
80## Manipulating Sendable Objects Across C++ Threads
81
82After implementing the capabilities to call ArkTS APIs from C++, serialize and deserialize objects for cross-thread transfer. The **napi_value** variable is not thread-safe and therefore cannot be directly shared across threads.
83
84The following code example demonstrates how to serialize and deserialize objects. Since Sendable objects are passed by reference, serialization does not create an additional copy of the data but directly passes the object reference to the deserializing thread. This makes serialization and deserialization more efficient than non-Sendable objects.
85
86ArkTS file definition
87
88```ts
89// SendableObjTest.ets
90@Sendable
91export class SendableObjTest {
92  static newSendable() {
93    return 1024;
94  }
95}
96```
97<!-- @[arkts_define_obj](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTs/ArkTsConcurrent/ApplicationMultithreadingDevelopment/NativeInterthreadShared/entry/src/main/ets/pages/SendableObjTest.ets) -->
98
99Native implementation for serialization and deserialization of Sendable objects
100
101```cpp
102// napi_init.cpp
103#include "napi/native_api.h"
104#include <thread>
105
106static void *serializationData = nullptr;
107static void *CreateEnvAndSendSendable(void *) {
108    // 1. Create the ArkTS runtime environment.
109    napi_env env = nullptr;
110    napi_status ret = napi_create_ark_runtime(&env);
111    if (ret != napi_ok) {
112        std::abort();
113    }
114    // 2. Load the custom module, assuming that SendableObjTest provides the newSendable function for creating Sendable objects.
115    napi_value test = nullptr;
116    ret = napi_load_module_with_info(env, "entry/src/main/ets/pages/SendableObjTest", "com.example.myapplication/entry",
117                                     &test);
118    if (ret != napi_ok) {
119        std::abort();
120    }
121    napi_value sendableObjTest = nullptr;
122    ret = napi_get_named_property(env, test, "SendableObjTest", &sendableObjTest);
123    if (ret != napi_ok) {
124        std::abort();
125    }
126    // 3. Use newSendable in ArkTS, assuming that the newSendable function in sendableObjTest returns a Sendable object.
127    napi_value newSendable = nullptr;
128    ret = napi_get_named_property(env, sendableObjTest, "newSendable", &newSendable);
129    if (ret != napi_ok) {
130        std::abort();
131    }
132    // 4. Call the newSendable function to return the newly created Sendable object and store it in result.
133    napi_value result = nullptr;
134    ret = napi_call_function(env, sendableObjTest, newSendable, 0, nullptr, &result);
135    if (ret != napi_ok) {
136        std::abort();
137    }
138    // 5. Serialize the Sendable object.
139    napi_value undefined;
140    napi_get_undefined(env, &undefined);
141    ret = napi_serialize(env, result, undefined, undefined, &serializationData);
142    if (ret != napi_ok) {
143        std::abort();
144    }
145    return nullptr;
146}
147
148static void *CreateEnvAndReceiveSendable(void *) {
149    // 1. Create the ArkTS runtime environment.
150    napi_env env = nullptr;
151    napi_status ret = napi_create_ark_runtime(&env);
152    if (ret != napi_ok) {
153        std::abort();
154    }
155    // 2. Deserialize the Sendable object and store it in result, which can then be manipulated via Node-API.
156    napi_value result = nullptr;
157    ret = napi_deserialize(env, serializationData, &result);
158    if (ret != napi_ok) {
159        std::abort();
160    }
161    // 3. Delete the serialization data.
162    ret = napi_delete_serialization_data(env, serializationData);
163    if (ret != napi_ok) {
164        std::abort();
165    }
166    napi_valuetype valuetype0;
167    napi_typeof(env, result, &valuetype0);
168    if (valuetype0 != napi_number) {
169        std::abort();
170    }
171    int value0;
172    napi_get_value_int32(env, result, &value0);
173    if (value0 != 1024) {
174        std::abort();
175    }
176    return nullptr;
177}
178
179static napi_value TestSendSendable([[maybe_unused]] napi_env env, [[maybe_unused]] napi_callback_info info) {
180    std::thread t1(CreateEnvAndSendSendable, nullptr);
181    t1.join();
182    std::thread t2(CreateEnvAndReceiveSendable, nullptr);
183    t2.join();
184    return nullptr;
185}
186
187EXTERN_C_START
188static napi_value Init(napi_env env, napi_value exports) {
189    napi_property_descriptor desc[] = {
190        {"testSendSendable", nullptr, TestSendSendable, nullptr, nullptr, nullptr, napi_default, nullptr}};
191    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
192    return exports;
193}
194EXTERN_C_END
195
196static napi_module demoModule = {
197    .nm_version = 1,
198    .nm_flags = 0,
199    .nm_filename = nullptr,
200    .nm_register_func = Init,
201    .nm_modname = "entry",
202    .nm_priv = ((void *)0),
203    .reserved = {0},
204};
205
206extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
207    napi_module_register(&demoModule);
208}
209```
210<!-- @[native_deserialize_sendable](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTs/ArkTsConcurrent/ApplicationMultithreadingDevelopment/NativeInterthreadShared/entry/src/main/cpp/napi_init.cpp) -->
211
212
213```
214// Index.d.ts
215export const testSendSendable: () => void;
216```
217<!-- @[native_deserialize_sendable](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTs/ArkTsConcurrent/ApplicationMultithreadingDevelopment/NativeInterthreadShared/entry/src/main/cpp/types/libentry/Index.d.ts) -->
218
219UI main thread invocation
220
221```ts
222// Index.ets
223import { hilog } from '@kit.PerformanceAnalysisKit';
224import testNapi from 'libentry.so';
225import { SendableObjTest } from './SendableObjTest'
226
227@Entry
228@Component
229struct Index {
230  @State message: string = 'Hello World';
231
232  build() {
233    Row() {
234      Column() {
235        Text(this.message)
236          .fontSize(50)
237          .fontWeight(FontWeight.Bold)
238          .onClick(() => {
239            SendableObjTest.newSendable()
240            hilog.info(0x0000, 'testTag', 'Test send Sendable begin');
241            testNapi.testSendSendable();
242            hilog.info(0x0000, 'testTag', 'Test send Sendable end');
243          })
244      }
245      .width('100%')
246    }
247    .height('100%')
248  }
249}
250```
251<!-- @[main_thread_init_call](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTs/ArkTsConcurrent/ApplicationMultithreadingDevelopment/NativeInterthreadShared/entry/src/main/ets/pages/Index.ets) -->
252
253The logic implementation of the entire process is as follows:
254
2551. In the UI main thread where the **main** function resides, create an ArkTS runtime environment and initiate a C++child thread to create a Sendable object and store the object in **result**. Serialize the Sendable object referenced by **result** into global serialization data **serializationData**.
256
2572. After the preceding steps are complete, initiate another C++ child thread. In this new thread, create the ArkTS runtime environment. Then, deserialize the Sendable object created in the UI main thread from **serializationData** using the deserialization interface and store it in **result**. This enables the transfer of Sendable objects across C++ thread. After deserialization, the deserialization data must be destroyed to prevent memory leaks. In this case, both the UI main thread and the child thread hold the Sendable object, which can be manipulated via Node-API, such as reading/writing or passing it to the ArkTS layer.
258
259   > **NOTE**
260   >
261   > The object being manipulated must comply with the rules of Sendable objects. For details, see [Usage Rules and Constraints for Sendable](sendable-constraints.md).
262
263<!--no_check-->