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
16 #include "test.h"
17
18 #include <uv.h>
19
20 #include "napi/native_api.h"
21 #include "napi/native_node_api.h"
22 #include "securec.h"
23 #include "utils/log.h"
24
25 struct CallJsCbData_t {
26 int32_t id = 0;
27 bool secondaryThread = false;
28 napi_threadsafe_function_call_mode blockOnFull = napi_tsfn_nonblocking;
29 };
30
31 struct FinalCbData_t {
32 int32_t id = 0;
33 };
34
35 static constexpr int32_t SEND_DATA_TEST = 11;
36 static constexpr int32_t CALL_JS_CB_DATA_TEST_ID = 101;
37 static constexpr int32_t FINAL_CB_DATA_TEST_ID = 1001;
38 static constexpr int32_t SEND_DATAS_LENGTH = 10;
39 static constexpr int32_t THREAD_COUNT = 2;
40 static constexpr int32_t THREAD_COUNT_FOUR = 4;
41 static constexpr int32_t MAX_QUEUE_SIZE = 3;
42 static constexpr int32_t SUCCESS_COUNT_JS_FOUR = 4;
43
44 static pid_t g_mainTid = 0;
45 static CallJsCbData_t g_jsData;
46 static CallJsCbData_t g_jsDataInternal;
47 static FinalCbData_t g_finalData;
48 static int32_t g_sendData = 0;
49 static uv_thread_t g_uvThread;
50 static uv_thread_t g_uvThreadTest5;
51 static uv_thread_t g_uvThreadTest6;
52 static uv_thread_t g_uvThreadTest7;
53 static uv_thread_t g_uvThreadSecondary;
54 static uv_thread_t g_uvTheads2;
55 static uv_thread_t g_uvTheads3;
56 static int32_t g_sendDatas[SEND_DATAS_LENGTH];
57 static int32_t callSuccessCount = 0;
58 static int32_t callSuccessCountJS = 0;
59 static int32_t callSuccessCountJSFour = 0;
60 bool acquireFlag = false;
61
TsFuncCallJs(napi_env env,napi_value tsfn_cb,void * context,void * data)62 static void TsFuncCallJs(napi_env env, napi_value tsfn_cb, void* context, void* data)
63 {
64 HILOG_INFO("TsFuncCallJs called");
65
66 EXPECT_EQ(gettid(), g_mainTid);
67
68 // expect context equal
69 EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);
70
71 // expect data equal
72 int* pData = (int32_t*)data;
73 EXPECT_EQ((*pData), SEND_DATA_TEST);
74 }
TsFuncCallJsTwo(napi_env env,napi_value tsfn_cb,void * context,void * data)75 static void TsFuncCallJsTwo(napi_env env, napi_value tsfn_cb, void* context, void* data)
76 {
77 HILOG_INFO("TsFuncCallJsTwo called");
78 TsFuncCallJs(env, tsfn_cb, context, data);
79 callSuccessCountJS++;
80 }
TsFuncCallJsFour(napi_env env,napi_value tsfn_cb,void * context,void * data)81 static void TsFuncCallJsFour(napi_env env, napi_value tsfn_cb, void* context, void* data)
82 {
83 HILOG_INFO("TsFuncCallJsFour called");
84
85 TsFuncCallJs(env, tsfn_cb, context, data);
86 callSuccessCountJSFour++;
87 }
TsFuncCallJsMulti(napi_env env,napi_value tsfn_cb,void * context,void * data)88 static void TsFuncCallJsMulti(napi_env env,
89 napi_value tsfn_cb,
90 void* context,
91 void* data)
92 {
93 HILOG_INFO("TsFuncCallJsMulti called");
94
95 EXPECT_EQ(gettid(), g_mainTid);
96
97 // expect context equal
98 EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);
99
100 int* pData = ((int32_t*)data);
101
102 HILOG_INFO("TsFuncCallJsMulti data %d", (*pData));
103 }
104
TsFuncFinal(napi_env env,void * finalizeData,void * hint)105 static void TsFuncFinal(napi_env env, void* finalizeData, void* hint)
106 {
107 HILOG_INFO("TsFuncFinal called");
108
109 // expect thread id equal
110 EXPECT_EQ(gettid(), g_mainTid);
111
112 // wait data source thread
113 uv_thread_join(&g_uvThread);
114
115 // expect context equal
116 EXPECT_EQ(((CallJsCbData_t*)hint)->id, CALL_JS_CB_DATA_TEST_ID);
117
118 // expect finalize data equal
119 EXPECT_EQ(((FinalCbData_t*)finalizeData)->id, FINAL_CB_DATA_TEST_ID);
120 }
TsFuncFinalTest5(napi_env env,void * finalizeData,void * hint)121 static void TsFuncFinalTest5(napi_env env, void* finalizeData, void* hint)
122 {
123 HILOG_INFO("TsFuncFinalTest5 called");
124
125 // expect thread id equal
126 EXPECT_EQ(gettid(), g_mainTid);
127
128 // wait data source thread
129 uv_thread_join(&g_uvThreadTest5);
130
131 // expect context equal
132 EXPECT_EQ(((CallJsCbData_t*)hint)->id, CALL_JS_CB_DATA_TEST_ID);
133
134 // expect finalize data equal
135 EXPECT_EQ(((FinalCbData_t*)finalizeData)->id, FINAL_CB_DATA_TEST_ID);
136 }
TsFuncFinalTotal(napi_env env,void * finalizeData,void * hint)137 static void TsFuncFinalTotal(napi_env env, void* finalizeData, void* hint)
138 {
139 HILOG_INFO("TsFuncFinalTotal called");
140 uv_thread_join(&g_uvThreadTest6);
141 // when add thread,repair callSuccessCountJS eq SUCCESS_COUNT_JS_TWO
142 EXPECT_EQ(callSuccessCountJS, SUCCESS_COUNT_JS_FOUR);
143 HILOG_INFO("TsFuncFinalTotal end");
144 }
TsFuncFinalTotalFour(napi_env env,void * finalizeData,void * hint)145 static void TsFuncFinalTotalFour(napi_env env, void* finalizeData, void* hint)
146 {
147 HILOG_INFO("TsFuncFinalTotalFour called");
148 uv_thread_join(&g_uvThreadTest7);
149 EXPECT_EQ(callSuccessCountJSFour, SUCCESS_COUNT_JS_FOUR);
150 HILOG_INFO("TsFuncFinalTotalFour end");
151 }
152
TsFuncFinalJoinThread(napi_env env,void * data,void * hint)153 static void TsFuncFinalJoinThread(napi_env env, void* data, void* hint)
154 {
155 HILOG_INFO("TsFuncFinalJoinThread called");
156
157 uv_thread_t *uvThread = reinterpret_cast<uv_thread_t*>(data);
158 CallJsCbData_t *jsData = reinterpret_cast<CallJsCbData_t*>(hint);
159
160 uv_thread_join(uvThread);
161
162 if (jsData->secondaryThread) {
163 uv_thread_join(&g_uvThreadSecondary);
164 }
165 }
166
TsFuncSecondaryThread(void * data)167 static void TsFuncSecondaryThread(void* data)
168 {
169 HILOG_INFO("TsFuncSecondaryThread called");
170
171 // expect thread id not equal
172 EXPECT_NE(gettid(), g_mainTid);
173
174 napi_threadsafe_function func = (napi_threadsafe_function)data;
175
176 auto status = napi_release_threadsafe_function(func, napi_tsfn_release);
177 EXPECT_EQ(status, napi_ok);
178 }
179
TsFuncDataSourceThread(void * data)180 static void TsFuncDataSourceThread(void* data)
181 {
182 HILOG_INFO("TsFuncDataSourceThread called");
183
184 // expect thread id not equal
185 EXPECT_NE(gettid(), g_mainTid);
186
187 napi_threadsafe_function func = (napi_threadsafe_function)data;
188 napi_threadsafe_function_call_mode blockMode = napi_tsfn_nonblocking;
189 void* context = nullptr;
190
191 auto status = napi_get_threadsafe_function_context(func, &context);
192 EXPECT_EQ(status, napi_ok);
193
194 // expect context equal
195 EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);
196
197 // set send data
198 g_sendData = SEND_DATA_TEST;
199
200 // As main thread has set initial_thread_count to 1 and only this one secondary thread,
201 // so no need to call `napi_acquire_threadsafe_function()`.
202 status = napi_call_threadsafe_function(func, &g_sendData, blockMode);
203 EXPECT_EQ(status, napi_ok);
204
205 status = napi_release_threadsafe_function(func, napi_tsfn_release);
206 EXPECT_EQ(status, napi_ok);
207 }
TsFuncDataSourceThreadAbort(void * data)208 static void TsFuncDataSourceThreadAbort(void* data)
209 {
210 HILOG_INFO("TsFuncDataSourceThreadAbort called");
211
212 // expect thread id not equal
213 EXPECT_NE(gettid(), g_mainTid);
214
215 napi_threadsafe_function func = (napi_threadsafe_function)data;
216 napi_threadsafe_function_call_mode blockMode = napi_tsfn_nonblocking;
217 void* context = nullptr;
218
219 auto status = napi_get_threadsafe_function_context(func, &context);
220 EXPECT_EQ(status, napi_ok);
221
222 // expect context equal
223 EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);
224
225 // set send data
226 g_sendData = SEND_DATA_TEST;
227
228 status = napi_call_threadsafe_function(func, &g_sendData, blockMode);
229 EXPECT_EQ(status, napi_closing);
230 }
231
TsFuncDataSourceThreadCountTotal(void * data)232 static void TsFuncDataSourceThreadCountTotal(void* data)
233 {
234 HILOG_INFO("TsFuncDataSourceThreadCountTotal called");
235
236 // expect thread id not equal
237 EXPECT_NE(gettid(), g_mainTid);
238
239 napi_threadsafe_function func = (napi_threadsafe_function)data;
240 napi_threadsafe_function_call_mode blockMode = napi_tsfn_nonblocking;
241 void* context = nullptr;
242
243 auto status = napi_get_threadsafe_function_context(func, &context);
244 EXPECT_EQ(status, napi_ok);
245
246 // expect context equal
247 EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);
248 // set send data
249 g_sendData = SEND_DATA_TEST;
250 if (acquireFlag) {
251 std::cout<<"acquireFlag is true"<<std::endl;
252 status = napi_acquire_threadsafe_function(func);
253 EXPECT_EQ(status, napi_ok);
254 status = napi_call_threadsafe_function(func, &g_sendData, blockMode);
255 if (status == napi_ok) {
256 callSuccessCount++;
257 }
258 status = napi_release_threadsafe_function(func, napi_tsfn_release);
259 } else {
260 status = napi_call_threadsafe_function(func, &g_sendData, blockMode);
261 if (status == napi_ok) {
262 callSuccessCount++;
263 }
264 }
265 status = napi_release_threadsafe_function(func, napi_tsfn_release);
266 }
267
TsFuncDataSourceThreadMulti(void * data)268 static void TsFuncDataSourceThreadMulti(void* data)
269 {
270 HILOG_INFO("TsFuncDataSourceThreadMulti called");
271
272 // expect thread id not equal
273 EXPECT_NE(gettid(), g_mainTid);
274
275 napi_threadsafe_function func = (napi_threadsafe_function)data;
276 void* context = nullptr;
277
278 auto status = napi_get_threadsafe_function_context(func, &context);
279 EXPECT_EQ(status, napi_ok);
280 CallJsCbData_t* jsData = nullptr;
281 jsData = (CallJsCbData_t*)context;
282
283 if (jsData->secondaryThread) {
284 status = napi_acquire_threadsafe_function(func);
285 EXPECT_EQ(status, napi_ok);
286
287 if (uv_thread_create(&g_uvThreadSecondary, TsFuncSecondaryThread, func) != 0) {
288 HILOG_ERROR("Failed to create uv thread!");
289 }
290 }
291
292 bool queueClosing = false;
293 bool queueFull = false;
294 int32_t index = 0;
295 for (index = SEND_DATAS_LENGTH - 1; index > -1 && !queueClosing; index--) {
296 g_sendDatas[index] = index;
297 status = napi_call_threadsafe_function(func, &g_sendDatas[index], jsData->blockOnFull);
298 HILOG_INFO("napi_call_threadsafe_function index %d status %d", index, status);
299
300 switch (status) {
301 case napi_queue_full:
302 queueFull = true;
303 index++;
304 [[fallthrough]];
305 case napi_ok:
306 continue;
307 case napi_closing:
308 queueClosing = true;
309 break;
310 default:
311 HILOG_ERROR("Failed to call napi_call_threadsafe_function!");
312 }
313 }
314
315 if (!queueClosing && (napi_release_threadsafe_function(func, napi_tsfn_release) != napi_ok)) {
316 HILOG_ERROR("Failed to call napi_release_threadsafe_function!");
317 }
318 }
319
TsFuncThreadInternal(napi_env env,napi_threadsafe_function_call_js cb,uv_thread_t & uvThread,bool secondary,bool block)320 static void TsFuncThreadInternal(napi_env env,
321 napi_threadsafe_function_call_js cb,
322 uv_thread_t& uvThread,
323 bool secondary,
324 bool block)
325 {
326 HILOG_INFO("TsFuncThreadInternal start cb %p secondary %d block %d", cb, secondary, block);
327
328 napi_threadsafe_function tsFunc = nullptr;
329 napi_value resourceName = 0;
330
331 napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
332 g_mainTid = gettid();
333
334 g_jsDataInternal.id = CALL_JS_CB_DATA_TEST_ID;
335 g_jsDataInternal.secondaryThread = (secondary ? true : false);
336 g_jsDataInternal.blockOnFull = (block ? napi_tsfn_blocking : napi_tsfn_nonblocking);
337
338 auto status = napi_create_threadsafe_function(env,
339 nullptr,
340 nullptr,
341 resourceName,
342 MAX_QUEUE_SIZE,
343 THREAD_COUNT,
344 &uvThread,
345 TsFuncFinalJoinThread,
346 &g_jsDataInternal,
347 cb,
348 &tsFunc);
349 EXPECT_EQ(status, napi_ok);
350
351 if (uv_thread_create(&uvThread, TsFuncDataSourceThreadMulti, tsFunc) != 0) {
352 HILOG_ERROR("Failed to create uv thread!");
353 }
354
355 HILOG_INFO("TsFuncThreadInternal end");
356 }
357
358 class NapiThreadsafeTest : public NativeEngineTest {
359 public:
SetUpTestCase()360 static void SetUpTestCase()
361 {
362 GTEST_LOG_(INFO) << "NapiThreadsafeTest SetUpTestCase";
363 }
364
TearDownTestCase()365 static void TearDownTestCase()
366 {
367 GTEST_LOG_(INFO) << "NapiThreadsafeTest TearDownTestCase";
368 }
369
SetUp()370 void SetUp() override {}
TearDown()371 void TearDown() override {}
372 };
373
374 /**
375 * @tc.name: ThreadsafeTest
376 * @tc.desc: Test LoadModule Func.
377 * @tc.type: FUNC
378 */
379 HWTEST_F(NapiThreadsafeTest, ThreadsafeTest001, testing::ext::TestSize.Level1)
380 {
381 HILOG_INFO("Threadsafe_Test_0100 start");
382 napi_env env = (napi_env)engine_;
383 napi_threadsafe_function tsFunc = nullptr;
384 napi_value resourceName = 0;
385
386 napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
387 g_mainTid = gettid();
388 g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
389 g_finalData.id = FINAL_CB_DATA_TEST_ID;
390
391 auto status = napi_create_threadsafe_function(env,
392 nullptr,
393 nullptr,
394 resourceName,
395 0,
396 1,
397 &g_finalData,
398 TsFuncFinal,
399 &g_jsData,
400 TsFuncCallJs,
401 &tsFunc);
402 EXPECT_EQ(status, napi_ok);
403
404 if (uv_thread_create(&g_uvThread, TsFuncDataSourceThread, tsFunc) != 0) {
405 HILOG_ERROR("Failed to create uv thread!");
406 }
407
408 HILOG_INFO("Threadsafe_Test_0100 end");
409 }
410
411 /**
412 * @tc.name: ThreadsafeTest
413 * @tc.desc: Test LoadModule Func.
414 * @tc.type: FUNC
415 */
416 HWTEST_F(NapiThreadsafeTest, ThreadsafeTest002, testing::ext::TestSize.Level1)
417 {
418 HILOG_INFO("Threadsafe_Test_0200 start");
419
420 // start secondary thread, block on full
421 TsFuncThreadInternal((napi_env)engine_, TsFuncCallJsMulti, g_uvTheads2, true, true);
422
423 HILOG_INFO("Threadsafe_Test_0200 end");
424 }
425
426 /**
427 * @tc.name: ThreadsafeTest
428 * @tc.desc: Test threadsafe Func, no js.
429 * @tc.type: FUNC
430 */
431 HWTEST_F(NapiThreadsafeTest, ThreadsafeTest003, testing::ext::TestSize.Level1)
432 {
433 HILOG_INFO("Threadsafe_Test_0300 start");
434
435 // secondary thread, not block
436 TsFuncThreadInternal((napi_env)engine_, TsFuncCallJsMulti, g_uvTheads3, false, false);
437
438 HILOG_INFO("Threadsafe_Test_0300 end");
439 }
440
441 /**
442 * @tc.name: ThreadsafeTest
443 * @tc.desc: Test napi_release_threadsafe_function, napi_tsfn_abort.
444 * @tc.type: FUNC
445 */
446 HWTEST_F(NapiThreadsafeTest, ThreadsafeTest004, testing::ext::TestSize.Level1)
447 {
448 HILOG_INFO("Threadsafe_Test_0400 start");
449 napi_env env = (napi_env)engine_;
450 napi_threadsafe_function tsFunc = nullptr;
451 napi_value resourceName = 0;
452
453 napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
454 g_mainTid = gettid();
455 g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
456 g_finalData.id = FINAL_CB_DATA_TEST_ID;
457
458 auto status = napi_create_threadsafe_function(env,
459 nullptr,
460 nullptr,
461 resourceName,
462 0,
463 10,
464 &g_finalData,
465 TsFuncFinalTest5,
466 &g_jsData,
467 TsFuncCallJs,
468 &tsFunc);
469 EXPECT_EQ(status, napi_ok);
470 status = napi_release_threadsafe_function(tsFunc, napi_tsfn_abort);
471 EXPECT_EQ(status, napi_ok);
472 if (uv_thread_create(&g_uvThreadTest5, TsFuncDataSourceThreadAbort, tsFunc) != 0) {
473 HILOG_ERROR("Failed to create uv thread!");
474 }
475
476 HILOG_INFO("Threadsafe_Test_0400 end");
477 }
478
479 /**
480 * @tc.name: ThreadsafeTest
481 * @tc.desc: Test initial_thread_count not enough.
482 * @tc.type: FUNC
483 */
484 HWTEST_F(NapiThreadsafeTest, ThreadsafeTest005, testing::ext::TestSize.Level1)
485 {
486 HILOG_INFO("Threadsafe_Test_0500 start");
487 napi_env env = (napi_env)engine_;
488 napi_threadsafe_function tsFunc = nullptr;
489 napi_value resourceName = 0;
490 callSuccessCountJS=0;
491 callSuccessCount=0;
492 napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
493 g_mainTid = gettid();
494 g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
495 g_finalData.id = FINAL_CB_DATA_TEST_ID;
496
497 auto status = napi_create_threadsafe_function(env,
498 nullptr,
499 nullptr,
500 resourceName,
501 0,
502 2,
503 &g_finalData,
504 TsFuncFinalTotal,
505 &g_jsData,
506 TsFuncCallJsTwo,
507 &tsFunc);
508 EXPECT_EQ(status, napi_ok);
509 int threadCount = THREAD_COUNT_FOUR;
510 acquireFlag = false;
511
512 for (int i = 0; i < threadCount; i++) {
513 if (uv_thread_create(&g_uvThreadTest6, TsFuncDataSourceThreadCountTotal, tsFunc) != 0) {
514 HILOG_ERROR("Failed to create uv thread!");
515 }
516 }
517
518 usleep(200 * 1000);
519 EXPECT_EQ(callSuccessCount, SUCCESS_COUNT_JS_FOUR);
520 HILOG_INFO("Threadsafe_Test_0500 end");
521 }
522 /**
523 * @tc.name: ThreadsafeTest
524 * @tc.desc: Test initial_thread_count not enough but acquire.
525 * @tc.type: FUNC
526 */
527 HWTEST_F(NapiThreadsafeTest, ThreadsafeTest006, testing::ext::TestSize.Level1)
528 {
529 HILOG_INFO("Threadsafe_Test_0600 start");
530 napi_env env = (napi_env)engine_;
531 napi_threadsafe_function tsFunc = nullptr;
532 napi_value resourceName = 0;
533 callSuccessCount=0;
534 napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
535 g_mainTid = gettid();
536 g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
537 g_finalData.id = FINAL_CB_DATA_TEST_ID;
538
539 auto status = napi_create_threadsafe_function(env,
540 nullptr,
541 nullptr,
542 resourceName,
543 0,
544 1,
545 &g_finalData,
546 TsFuncFinalTotalFour,
547 &g_jsData,
548 TsFuncCallJsFour,
549 &tsFunc);
550 EXPECT_EQ(status, napi_ok);
551 int threadCount = THREAD_COUNT_FOUR;
552 acquireFlag=true;
553
554 for (int i = 0; i < threadCount; i++) {
555 if (uv_thread_create(&g_uvThreadTest7, TsFuncDataSourceThreadCountTotal, tsFunc) != 0) {
556 HILOG_ERROR("Failed to create uv thread!");
557 }
558 }
559
560 usleep(200 * 1000);
561 EXPECT_EQ(callSuccessCount, SUCCESS_COUNT_JS_FOUR);
562 HILOG_INFO("Threadsafe_Test_0600 end");
563 }