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