1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "napi/native_api.h"
17 #include "hilog/log.h"
18 #include <future>
19
20 static constexpr int INT_ARG_2 = 2; // 入参索引
21 static constexpr int INT_BUF_32 = 32; // 入参索引
22
23 struct CallbackData {
24 napi_threadsafe_function tsfn;
25 napi_async_work work;
26 };
27
ExecuteWork(napi_env env,void * data)28 static void ExecuteWork(napi_env env, void *data)
29 {
30 CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
31 std::promise<std::string> promise;
32 auto future = promise.get_future();
33 napi_call_threadsafe_function(callbackData->tsfn, &promise, napi_tsfn_nonblocking);
34 try {
35 auto result = future.get();
36 OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", result.c_str());
37 } catch (const std::exception &e) {
38 OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", e.what());
39 }
40 }
41
ResolvedCallback(napi_env env,napi_callback_info info)42 static napi_value ResolvedCallback(napi_env env, napi_callback_info info)
43 {
44 void *data = nullptr;
45 size_t argc = 1;
46 napi_value argv[1];
47 if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) {
48 return nullptr;
49 }
50 size_t result = 0;
51 char buf[32] = {0};
52 napi_get_value_string_utf8(env, argv[0], buf, INT_BUF_32, &result);
53 reinterpret_cast<std::promise<std::string> *>(data)->set_value(std::string(buf));
54 return nullptr;
55 }
56
RejectedCallback(napi_env env,napi_callback_info info)57 static napi_value RejectedCallback(napi_env env, napi_callback_info info)
58 {
59 void *data = nullptr;
60 if (napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data) != napi_ok) {
61 return nullptr;
62 }
63 reinterpret_cast<std::promise<std::string> *>(data)->set_exception(
64 std::make_exception_ptr(std::runtime_error("Error in jsCallback")));
65 return nullptr;
66 }
67
CallJs(napi_env env,napi_value jsCb,void * context,void * data)68 static void CallJs(napi_env env, napi_value jsCb, void *context, void *data)
69 {
70 if (env == nullptr) {
71 return;
72 }
73 napi_value undefined = nullptr;
74 napi_value promise = nullptr;
75 napi_get_undefined(env, &undefined);
76 napi_call_function(env, undefined, jsCb, 0, nullptr, &promise);
77 napi_value thenFunc = nullptr;
78 if (napi_get_named_property(env, promise, "then", &thenFunc) != napi_ok) {
79 return;
80 }
81 napi_value resolvedCallback;
82 napi_value rejectedCallback;
83 napi_create_function(env, "resolvedCallback", NAPI_AUTO_LENGTH, ResolvedCallback, data, &resolvedCallback);
84 napi_create_function(env, "rejectedCallback", NAPI_AUTO_LENGTH, RejectedCallback, data, &rejectedCallback);
85 napi_value argv[2] = {resolvedCallback, rejectedCallback};
86 napi_call_function(env, promise, thenFunc, INT_ARG_2, argv, nullptr);
87 }
88
WorkComplete(napi_env env,napi_status status,void * data)89 static void WorkComplete(napi_env env, napi_status status, void *data)
90 {
91 CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
92 napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release);
93 napi_delete_async_work(env, callbackData->work);
94 callbackData->tsfn = nullptr;
95 callbackData->work = nullptr;
96 }
97
98
StartThread(napi_env env,napi_callback_info info)99 static napi_value StartThread(napi_env env, napi_callback_info info)
100 {
101 size_t argc = 1;
102 napi_value jsCb = nullptr;
103 CallbackData *callbackData = nullptr;
104 napi_get_cb_info(env, info, &argc, &jsCb, nullptr, reinterpret_cast<void **>(&callbackData));
105
106 // 创建一个线程安全函数
107 napi_value resourceName = nullptr;
108 napi_create_string_utf8(env, "Thread-safe Function Demo", NAPI_AUTO_LENGTH, &resourceName);
109 napi_create_threadsafe_function(env, jsCb, nullptr, resourceName, 0, 1, callbackData, nullptr, callbackData, CallJs,
110 &callbackData->tsfn);
111
112 // 创建一个异步任务
113 // ExecuteWork会执行在一个由libuv创建的非JS线程上
114 // 此处使用napi_create_async_work是为了模拟在非JS线程场景使用napi_call_threadsafe_function接口向JS线程提交任务
115 napi_create_async_work(env, nullptr, resourceName, ExecuteWork, WorkComplete, callbackData, &callbackData->work);
116
117 // 将异步任务加入到异步队列中
118 napi_queue_async_work(env, callbackData->work);
119 return nullptr;
120 }
121
122
123 EXTERN_C_START
Init(napi_env env,napi_value exports)124 static napi_value Init(napi_env env, napi_value exports)
125 {
126 CallbackData *callbackData = new CallbackData(); // 可在线程退出时释放
127 napi_property_descriptor desc[] = {
128 {"startThread", nullptr, StartThread, nullptr, nullptr, nullptr, napi_default, callbackData},
129 };
130 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
131 return exports;
132 }
133 EXTERN_C_END
134
135 static napi_module demoModule = {
136 .nm_version = 1,
137 .nm_flags = 0,
138 .nm_filename = nullptr,
139 .nm_register_func = Init,
140 .nm_modname = "entry1",
141 .nm_priv = ((void *)0),
142 .reserved = {0},
143 };
144
RegisterEntryModule(void)145 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
146