• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 #include <uv.h>
16 #include "js_napi_common.h"
17 #include "napi/native_api.h"
18 #include "napi/native_common.h"
19 #include "napi/native_node_api.h"
20 #include "utils/log.h"
21 namespace ACE {
22 namespace NAPI {
23 namespace SYSTEM_TEST_NAPI {
24 static constexpr int ARRAY_LENGTH = 10;
25 static constexpr int MAX_QUEUE_SIZE = 2;
26 static constexpr int MAX_THREAD_SIZE = 2;
27 static constexpr int THREAD_ARG_TWO = 2;
28 static constexpr int THREAD_ARG_THREE = 3;
29 static constexpr int THREAD_ARG_FOUR = 4;
30 
31 static uv_thread_t uv_threads[MAX_THREAD_SIZE];
32 static napi_threadsafe_function tsfun;
33 
34 struct TS_FN_HINT {
35     napi_threadsafe_function_call_mode blockOnFull = napi_tsfn_blocking;
36     napi_threadsafe_function_release_mode abort = napi_tsfn_abort;
37     bool startSecondary = false;
38     napi_ref jsFinalizeCallBackRef = nullptr;
39     uint32_t maxQueueSize = 0;
40 };
41 using TsFnHint = struct TS_FN_HINT;
42 
43 static TsFnHint tsinfo;
44 
45 // Thread data to transmit to JS
46 static int transmitData[ARRAY_LENGTH];
47 static napi_ref testCallbackRef[ARRAY_LENGTH] = { nullptr };
48 
ReleaseThreadsafeFunction(void * data)49 static void ReleaseThreadsafeFunction(void* data)
50 {
51     HILOG_INFO("%{public}s,called", __func__);
52     napi_threadsafe_function tsfun = static_cast<napi_threadsafe_function>(data);
53 
54     if (napi_release_threadsafe_function(tsfun, napi_tsfn_release) != napi_ok) {
55         napi_fatal_error("ReleaseThreadsafeFunction",
56             NAPI_AUTO_LENGTH, "napi_release_threadsafe_function failed", NAPI_AUTO_LENGTH);
57     }
58 }
59 static napi_env gCallEnv = nullptr;
60 // Source thread producing the data
DataSourceThread(void * data)61 static void DataSourceThread(void* data)
62 {
63     HILOG_INFO("%{public}s,called start", __func__);
64     napi_env env = gCallEnv;
65     napi_threadsafe_function tsfun = static_cast<napi_threadsafe_function>(data);
66     void* hint = nullptr;
67     bool queueWasFull = false;
68     bool queueWasClosing = false;
69     NAPI_CALL_RETURN_VOID(env, napi_get_threadsafe_function_context(tsfun, &hint));
70 
71     TsFnHint* tsFnInfo = static_cast<TsFnHint*>(hint);
72     if (tsFnInfo != &tsinfo) {
73         napi_fatal_error("DataSourceThread", NAPI_AUTO_LENGTH,
74             "thread-safe function hint is not as expected", NAPI_AUTO_LENGTH);
75     }
76     if (tsFnInfo->startSecondary) {
77         NAPI_CALL_RETURN_VOID(env, napi_acquire_threadsafe_function(tsfun));
78         if (uv_thread_create(&uv_threads[1], ReleaseThreadsafeFunction, tsfun) != 0) {
79             napi_fatal_error("DataSourceThread", NAPI_AUTO_LENGTH,
80                 "failed to start secondary thread", NAPI_AUTO_LENGTH);
81         }
82     }
83     for (int index = ARRAY_LENGTH - 1; index > -1 && !queueWasClosing; index--) {
84         auto status = napi_call_threadsafe_function(tsfun, &transmitData[index], tsFnInfo->blockOnFull);
85         std::string statusStr;
86         switch (status) {
87             case napi_queue_full:
88                 queueWasFull = true;
89                 index++;
90                 statusStr = "napi_queue_full";
91                 break;
92             case napi_ok:
93                 statusStr = "napi_ok";
94                 continue;
95             case napi_closing:
96                 statusStr = "napi_closing";
97                 queueWasClosing = true;
98                 break;
99             default:
100                 napi_fatal_error("DataSourceThread", NAPI_AUTO_LENGTH,
101                     "napi_call_threadsafe_function failed", NAPI_AUTO_LENGTH);
102         }
103         HILOG_INFO("%{public}s,called napi_call_threadsafe_function index = %{public}d, status =%{public}s",
104             __func__, index, statusStr.c_str());
105     }
106     if (!queueWasClosing && napi_release_threadsafe_function(tsfun, napi_tsfn_release) != napi_ok) {
107         napi_fatal_error(
108             "DataSourceThread", NAPI_AUTO_LENGTH, "napi_release_threadsafe_function failed", NAPI_AUTO_LENGTH);
109     }
110     HILOG_INFO("%{public}s,called end", __func__);
111 }
112 
113 // Getting the data into JS
CallJsFuntion(napi_env env,napi_value cb,void * hint,void * data)114 static void CallJsFuntion(napi_env env, napi_value cb, void* hint, void* data)
115 {
116     HILOG_INFO("%{public}s called", __func__);
117     if (!(env == nullptr || cb == nullptr)) {
118         napi_value argv = nullptr;
119         NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, *(int*)data, &argv));
120 
121         for (int i = ARRAY_LENGTH - 1; i >= 0; i--) {
122             if (testCallbackRef[i]) {
123                 napi_value callback = 0;
124                 napi_value undefined = nullptr;
125                 napi_value result = nullptr;
126                 napi_get_undefined(env, &undefined);
127                 napi_get_reference_value(env, testCallbackRef[i], &callback);
128                 napi_call_function(env, undefined, callback, 1, &argv, &result);
129                 napi_delete_reference(env, testCallbackRef[i]);
130                 testCallbackRef[i] = nullptr;
131                 break;
132             }
133         }
134     }
135 }
136 
137 static napi_ref altRef = nullptr;
138 
139 // Cleanup Param:jsFinalizeCallBack, abort
StopThread(napi_env env,napi_callback_info info)140 static napi_value StopThread(napi_env env, napi_callback_info info)
141 {
142     HILOG_INFO("%{public}s,called", __func__);
143 
144     uv_thread_join(&uv_threads[0]);
145     if (tsinfo.startSecondary) {
146         uv_thread_join(&uv_threads[1]);
147     }
148 
149     size_t argc = THREAD_ARG_TWO;
150     napi_value argv[THREAD_ARG_TWO] = { nullptr };
151     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
152     napi_valuetype value_type;
153     NAPI_CALL(env, napi_typeof(env, argv[0], &value_type));
154     NAPI_ASSERT(env, value_type == napi_function, "StopThread argument is a function");
155     NAPI_ASSERT(env, (tsfun != nullptr), "Existing threadsafe function");
156     NAPI_CALL(env, napi_create_reference(env, argv[0], 1, &(tsinfo.jsFinalizeCallBackRef)));
157     bool abort = false;
158     NAPI_CALL(env, napi_get_value_bool(env, argv[1], &abort));
159     NAPI_CALL(env, napi_release_threadsafe_function(tsfun, abort ? napi_tsfn_abort : napi_tsfn_release));
160     tsfun = nullptr;
161     return nullptr;
162 }
163 
164 // Join the thread and inform JS that we're done.
FinalizeCallBack(napi_env env,void * data,void * hint)165 static void FinalizeCallBack(napi_env env, void* data, void* hint)
166 {
167     HILOG_INFO("%{public}s,called", __func__);
168 
169     TsFnHint* theHint = static_cast<TsFnHint*>(hint);
170     napi_value jsCallback = nullptr;
171     napi_value undefined = nullptr;
172     napi_value result = nullptr;
173 
174     NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, theHint->jsFinalizeCallBackRef, &jsCallback));
175     NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
176     NAPI_CALL_RETURN_VOID(env, napi_call_function(env, undefined, jsCallback, 0, nullptr, &result));
177     NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, theHint->jsFinalizeCallBackRef));
178     if (altRef != nullptr) {
179         NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, altRef));
180         altRef = nullptr;
181     }
182 }
183 // jsfunc, abort/release, startSecondary, maxQueueSize
StartThreadInternal(napi_env env,napi_callback_info info,napi_threadsafe_function_call_js cb,bool blockOnFull,bool altRefJSCallBack)184 static napi_value StartThreadInternal(napi_env env, napi_callback_info info,
185     napi_threadsafe_function_call_js cb, bool blockOnFull, bool altRefJSCallBack)
186 {
187     HILOG_INFO("%{public}s,called start", __func__);
188     gCallEnv = env;
189     size_t argc = THREAD_ARG_FOUR;
190     napi_value argv[THREAD_ARG_FOUR] = { 0 };
191 
192     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
193     if (altRefJSCallBack) {
194         NAPI_CALL(env, napi_create_reference(env, argv[0], 1, &altRef));
195         argv[0] = nullptr;
196     }
197 
198     for (int i = 0; i<ARRAY_LENGTH; i++) {
199         napi_create_reference(env, argv[0], 1, &testCallbackRef[i]);
200     }
201 
202     tsinfo.blockOnFull = (blockOnFull ? napi_tsfn_blocking : napi_tsfn_nonblocking);
203 
204     NAPI_ASSERT(env, (tsfun == nullptr), "Existing thread-safe function");
205     napi_value asyncName;
206     NAPI_CALL(env, napi_create_string_utf8(env, "N-API Thread-safe Function Test", NAPI_AUTO_LENGTH, &asyncName));
207     NAPI_CALL(env, napi_get_value_uint32(env, argv[THREAD_ARG_THREE], &tsinfo.maxQueueSize));
208 
209     NAPI_CALL(env, napi_create_threadsafe_function(env, argv[0], nullptr, asyncName, tsinfo.maxQueueSize,
210         MAX_THREAD_SIZE, uv_threads, FinalizeCallBack, &tsinfo, cb, &tsfun));
211     bool abort = false;
212     NAPI_CALL(env, napi_get_value_bool(env, argv[1], &abort));
213     tsinfo.abort = abort ? napi_tsfn_abort : napi_tsfn_release;
214     NAPI_CALL(env, napi_get_value_bool(env, argv[THREAD_ARG_TWO], &(tsinfo.startSecondary)));
215 
216     NAPI_ASSERT(env, (uv_thread_create(&uv_threads[0], DataSourceThread, tsfun) == 0), "Thread creation");
217     HILOG_INFO("%{public}s,called end", __func__);
218     return nullptr;
219 }
220 
221 // Startup  param: jsfunc, abort/release, startSecondary, maxQueueSize
StartThread(napi_env env,napi_callback_info info)222 static napi_value StartThread(napi_env env, napi_callback_info info)
223 {
224     HILOG_INFO("%{public}s,called", __func__);
225     // blockOnFull:true  altRefJSCallBack:false
226     return StartThreadInternal(env, info, CallJsFuntion, true, false);
227 }
228 
StartThreadNonblocking(napi_env env,napi_callback_info info)229 static napi_value StartThreadNonblocking(napi_env env, napi_callback_info info)
230 {
231     HILOG_INFO("%{public}s,called", __func__);
232     // blockOnFull:false  altRefJSCallBack:false
233     return StartThreadInternal(env, info, CallJsFuntion, false, false);
234 }
235 
236 // Module init
ThreadSafeInit(napi_env env,napi_value exports)237 napi_value ThreadSafeInit(napi_env env, napi_value exports)
238 {
239     HILOG_INFO("%{public}s,called", __func__);
240 
241     for (size_t index = 0; index < ARRAY_LENGTH; index++) {
242         transmitData[index] = index;
243     }
244     napi_value jsArrayLength = 0;
245     napi_value jsMaxQueueSize = 0;
246     napi_create_uint32(env, ARRAY_LENGTH, &jsArrayLength);
247     napi_create_uint32(env, MAX_QUEUE_SIZE, &jsMaxQueueSize);
248 
249     napi_property_descriptor properties[] = {
250         DECLARE_NAPI_FUNCTION("testStartThread", StartThread),
251         DECLARE_NAPI_FUNCTION("testStartThreadNonblocking", StartThreadNonblocking),
252         DECLARE_NAPI_FUNCTION("testStopThread", StopThread),
253     };
254 
255     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(properties[0]), properties));
256     return exports;
257 }
258 } // namespace SYSTEM_TEST_NAPI
259 } // namespace NAPI
260 } // namespace ACE
261