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-->