1 /*
2 * Copyright (c) 2024 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 <unistd.h>
16 #include <sys/syscall.h>
17
18 #include <ctime>
19 #include <latch>
20 #include <thread>
21 #include <gtest/gtest.h>
22
23 #include "ark_native_engine.h"
24 #include "locks/async_lock.h"
25 #include "locks/async_lock_manager.h"
26 #include "locks/lock_request.h"
27 #include "test/unittest/common/test_common.h"
28
29 using namespace Commonlibrary::Concurrent::LocksModule;
30
31 class LocksTest : public testing::Test {
32 public:
SetUpTestSuite()33 static void SetUpTestSuite()
34 {
35 InitializeEngine();
36 }
37
TearDownTestSuite()38 static void TearDownTestSuite()
39 {
40 DestroyEngine();
41 }
42
InitializeEngine()43 static void InitializeEngine()
44 {
45 panda::RuntimeOption option;
46 option.SetGcType(panda::RuntimeOption::GC_TYPE::GEN_GC);
47
48 const int64_t poolSize = 0x1000000; // 16M
49 option.SetGcPoolSize(poolSize);
50
51 option.SetLogLevel(panda::RuntimeOption::LOG_LEVEL::ERROR);
52 option.SetDebuggerLibraryPath("");
53 vm_ = panda::JSNApi::CreateJSVM(option);
54 ASSERT_TRUE(vm_ != nullptr);
55
56 engine_ = new ArkNativeEngine(vm_, nullptr);
57 InitLocks();
58 }
59
InitLocks()60 static void InitLocks()
61 {
62 napi_env env {GetEnv()};
63 napi_value exports;
64 ASSERT_CHECK_CALL(napi_create_object(env, &exports));
65 AsyncLockManager::Init(env, exports);
66
67 napi_value locks;
68 ASSERT_CHECK_CALL(napi_get_named_property(env, exports, "locks", &locks));
69 ASSERT_CHECK_CALL(napi_get_named_property(env, locks, "AsyncLock", &asyncLockClass_));
70 ASSERT_CHECK_CALL(napi_get_named_property(env, locks, "AsyncLockOptions", &asyncLockOptions_));
71 ASSERT_CHECK_CALL(napi_get_named_property(env, asyncLockClass_, "request", &asyncLockRequest_));
72 ASSERT_CHECK_CALL(napi_create_int32(env, LockMode::LOCK_MODE_SHARED, &sharedMode_));
73 ASSERT_CHECK_CALL(napi_create_int32(env, LockMode::LOCK_MODE_EXCLUSIVE, &exclusiveMode_));
74 ASSERT_CHECK_CALL(napi_get_undefined(env, &undefined_));
75 }
76
TriggerGC()77 static void TriggerGC()
78 {
79 panda::JSNApi::TriggerGC(vm_, panda::JSNApi::TRIGGER_GC_TYPE::FULL_GC);
80 panda::JSNApi::TriggerGC(vm_, panda::JSNApi::TRIGGER_GC_TYPE::SHARED_FULL_GC);
81 }
82
83 static const uint64_t defaultTimeout {100};
84
SetUp()85 void SetUp() override
86 {
87 napi_env env {GetEnv()};
88 ASSERT_CHECK_CALL(napi_open_handle_scope(env, &scope_));
89 }
90
TearDown()91 void TearDown() override
92 {
93 napi_env env {GetEnv()};
94 napi_value exception;
95 ASSERT_CHECK_CALL(napi_get_and_clear_last_exception(env, &exception));
96 ASSERT_CHECK_CALL(napi_close_handle_scope(env, scope_));
97 }
98
DestroyEngine()99 static void DestroyEngine()
100 {
101 delete engine_;
102 engine_ = nullptr;
103 panda::JSNApi::DestroyJSVM(vm_);
104 }
105
GetEnv()106 static napi_env GetEnv()
107 {
108 return reinterpret_cast<napi_env>(engine_);
109 }
110
Loop(LoopMode mode)111 static void Loop(LoopMode mode)
112 {
113 engine_->Loop(mode);
114 }
115
116 template <typename P>
LoopUntil(const P & pred)117 static void LoopUntil(const P &pred)
118 {
119 static constexpr size_t timeoutNs = 10000000UL;
120 timespec timeout = {0, timeoutNs};
121 while (!pred()) {
122 Loop(LOOP_NOWAIT);
123 nanosleep(&timeout, nullptr);
124 }
125 }
126
CreateFunction(const char * name,napi_value (* callback)(napi_env,napi_callback_info),void * data=nullptr)127 static napi_value CreateFunction(const char *name, napi_value (*callback)(napi_env, napi_callback_info),
128 void *data = nullptr)
129 {
130 napi_value result;
131 napi_status status = napi_create_function(GetEnv(), name, NAPI_AUTO_LENGTH, callback, data, &result);
132 EXPECT_EQ(status, napi_ok);
133 return result;
134 }
135
Sleep()136 static void Sleep()
137 {
138 timespec ts{0, 100U * 1000U * 1000U}; // 100ms
139 nanosleep(&ts, nullptr);
140 }
141
142 protected:
143 static thread_local NativeEngine *engine_;
144 static thread_local EcmaVM *vm_;
145 static thread_local napi_value asyncLockClass_;
146 static thread_local napi_value asyncLockOptions_;
147 static thread_local napi_value asyncLockRequest_;
148 static thread_local napi_value sharedMode_;
149 static thread_local napi_value exclusiveMode_;
150 static thread_local napi_value undefined_;
151
152 private:
153 napi_handle_scope scope_ {nullptr};
154 };
155
156 thread_local NativeEngine *LocksTest::engine_ = nullptr;
157 thread_local EcmaVM *LocksTest::vm_ = nullptr;
158 thread_local napi_value LocksTest::asyncLockClass_ {nullptr};
159 thread_local napi_value LocksTest::asyncLockOptions_ {nullptr};
160 thread_local napi_value LocksTest::asyncLockRequest_ {nullptr};
161 thread_local napi_value LocksTest::sharedMode_ {nullptr};
162 thread_local napi_value LocksTest::exclusiveMode_ {nullptr};
163 thread_local napi_value LocksTest::undefined_ {nullptr};
164
ExclusiveLockSingleCb(napi_env env,napi_callback_info info)165 static napi_value ExclusiveLockSingleCb(napi_env env, napi_callback_info info)
166 {
167 bool *isCalled = nullptr;
168 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&isCalled));
169 *isCalled = true;
170 napi_value undefined;
171 napi_get_undefined(env, &undefined);
172 return undefined;
173 }
174
TEST_F(LocksTest,ExclusiveLockSingle)175 TEST_F(LocksTest, ExclusiveLockSingle)
176 {
177 napi_env env = GetEnv();
178 std::unique_ptr<AsyncLock> lock = std::make_unique<AsyncLock>(1);
179 bool isCalled = false;
180 napi_value callback = CreateFunction("exclusivelocksingle", ExclusiveLockSingleCb, &isCalled);
181 napi_ref callback_ref;
182 napi_create_reference(env, callback, 1, &callback_ref);
183
184 LockOptions options;
185 napi_value result = lock->LockAsync(env, callback_ref, LOCK_MODE_EXCLUSIVE, options);
186 bool isPromise = false;
187 napi_is_promise(env, result, &isPromise);
188 ASSERT_TRUE(isPromise);
189 Loop(LOOP_ONCE);
190 ASSERT_TRUE(isCalled);
191 }
192
193 struct CallbackData {
194 std::atomic<bool> executing = false;
195 std::atomic<bool> fail = false;
196 std::atomic<uint32_t> callCount = 0;
197 };
198
ExclusiveLockMultiCb(napi_env env,napi_callback_info info)199 static napi_value ExclusiveLockMultiCb(napi_env env, napi_callback_info info)
200 {
201 napi_value undefined;
202 napi_get_undefined(env, &undefined);
203 CallbackData *data = nullptr;
204 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
205 data->callCount += 1;
206 bool prev = data->executing.exchange(true);
207 if (prev) {
208 // The callback is executing now by another thread.
209 // Fail the test
210 data->fail = true;
211 return undefined;
212 }
213
214 LocksTest::Sleep();
215
216 data->executing = false;
217 return undefined;
218 }
219
TEST_F(LocksTest,ExclusiveLockMulti)220 TEST_F(LocksTest, ExclusiveLockMulti)
221 {
222 std::unique_ptr<AsyncLock> lock = std::make_unique<AsyncLock>(1);
223 AsyncLock *lockPtr = lock.get();
224 CallbackData callbackData;
225 std::thread t([lockPtr, &callbackData] () {
226 LocksTest::InitializeEngine();
227 napi_env env = GetEnv();
228 napi_value callback = CreateFunction("exclusivelockmulti", ExclusiveLockMultiCb, &callbackData);
229 napi_ref callback_ref;
230 napi_create_reference(env, callback, 1, &callback_ref);
231 LockOptions options;
232 lockPtr->LockAsync(env, callback_ref, LOCK_MODE_EXCLUSIVE, options);
233 Loop(LOOP_ONCE);
234 LocksTest::DestroyEngine();
235 });
236 napi_env env = GetEnv();
237 napi_value callback = CreateFunction("exclusivelockmulti", ExclusiveLockMultiCb, &callbackData);
238 napi_ref callback_ref;
239 napi_create_reference(env, callback, 1, &callback_ref);
240
241 LockOptions options;
242 lock->LockAsync(env, callback_ref, LOCK_MODE_EXCLUSIVE, options);
243 Loop(LOOP_ONCE);
244 t.join();
245 ASSERT_EQ(callbackData.callCount, 2U);
246 ASSERT_FALSE(callbackData.fail);
247 }
248
TEST_F(LocksTest,SharedLockSingle)249 TEST_F(LocksTest, SharedLockSingle)
250 {
251 napi_env env = GetEnv();
252 std::unique_ptr<AsyncLock> lock = std::make_unique<AsyncLock>(1);
253 bool isCalled = false;
254 napi_value callback = CreateFunction("sharedlocksingle", ExclusiveLockSingleCb, &isCalled);
255 napi_ref callback_ref;
256 napi_create_reference(env, callback, 1, &callback_ref);
257
258 LockOptions options;
259 lock->LockAsync(env, callback_ref, LOCK_MODE_SHARED, options);
260 Loop(LOOP_ONCE);
261 ASSERT_TRUE(isCalled);
262 }
263
264 struct SharedMultiCallbackData: public CallbackData {
SharedMultiCallbackDataSharedMultiCallbackData265 explicit SharedMultiCallbackData(std::latch &barrier): CallbackData(), barrier(barrier)
266 {
267 }
268
269 std::latch &barrier;
270 };
271
MainSharedLockMultiCb(napi_env env,napi_callback_info info)272 static napi_value MainSharedLockMultiCb(napi_env env, napi_callback_info info)
273 {
274 napi_value undefined;
275 napi_get_undefined(env, &undefined);
276 SharedMultiCallbackData *data = nullptr;
277 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
278 data->barrier.arrive_and_wait();
279 data->callCount += 1;
280
281 LocksTest::Sleep();
282 data->executing.exchange(true);
283
284 if (data->callCount != 2) {
285 data->fail = true;
286 return undefined;
287 }
288
289 data->executing = false;
290 return undefined;
291 }
292
SharedLockMultiCb(napi_env env,napi_callback_info info)293 static napi_value SharedLockMultiCb(napi_env env, napi_callback_info info)
294 {
295 napi_value undefined;
296 napi_get_undefined(env, &undefined);
297 CallbackData *data = nullptr;
298 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
299 data->callCount += 1;
300
301 LocksTest::Sleep();
302
303 if (data->callCount != 2) {
304 data->fail = true;
305 return undefined;
306 }
307
308 data->executing = false;
309 return undefined;
310 }
311
TEST_F(LocksTest,SharedLockMulti)312 TEST_F(LocksTest, SharedLockMulti)
313 {
314 std::unique_ptr<AsyncLock> lock = std::make_unique<AsyncLock>(1);
315 AsyncLock *lockPtr = lock.get();
316 std::latch barrier(2U);
317 SharedMultiCallbackData callbackData(barrier);
318 std::thread t([lockPtr, &callbackData, &barrier] () {
319 LocksTest::InitializeEngine();
320 napi_env env = GetEnv();
321 napi_value callback = CreateFunction("sharedlockmulti", SharedLockMultiCb, &callbackData);
322 napi_ref callback_ref;
323 napi_create_reference(env, callback, 1, &callback_ref);
324 LockOptions options;
325 barrier.arrive_and_wait();
326 lockPtr->LockAsync(env, callback_ref, LOCK_MODE_SHARED, options);
327 Loop(LOOP_ONCE);
328 LocksTest::DestroyEngine();
329 });
330 napi_env env = GetEnv();
331 napi_value callback = CreateFunction("sharedlockmulti", MainSharedLockMultiCb, &callbackData);
332 napi_ref callback_ref;
333 napi_create_reference(env, callback, 1, &callback_ref);
334
335 LockOptions options;
336 lock->LockAsync(env, callback_ref, LOCK_MODE_SHARED, options);
337 Loop(LOOP_ONCE);
338 t.join();
339 ASSERT_FALSE(callbackData.fail);
340 ASSERT_EQ(callbackData.callCount, 2U);
341 }
342
343 struct IsAvailableCallbackData {
IsAvailableCallbackDataIsAvailableCallbackData344 IsAvailableCallbackData(std::latch &b, std::latch &e): begin(b), end(e)
345 {
346 }
347
348 std::atomic<uint32_t> callCount = 0;
349 std::latch &begin;
350 std::latch &end;
351 };
352
IsAvailableCb(napi_env env,napi_callback_info info)353 static napi_value IsAvailableCb(napi_env env, napi_callback_info info)
354 {
355 IsAvailableCallbackData *data = nullptr;
356 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
357 data->callCount += 1;
358 data->begin.arrive_and_wait();
359 data->end.arrive_and_wait();
360 napi_value undefined;
361 napi_get_undefined(env, &undefined);
362 return undefined;
363 }
364
TEST_F(LocksTest,IsAvailable)365 TEST_F(LocksTest, IsAvailable)
366 {
367 std::unique_ptr<AsyncLock> lock = std::make_unique<AsyncLock>(1);
368 AsyncLock *lockPtr = lock.get();
369 std::latch begin(2U);
370 std::latch end(2U);
371 IsAvailableCallbackData data(begin, end);
372 std::thread t([lockPtr, &data] () {
373 LocksTest::InitializeEngine();
374 data.begin.arrive_and_wait();
375 napi_env env = GetEnv();
376 napi_value callback = CreateFunction("isavailable", IsAvailableCb, &data);
377 napi_ref callback_ref;
378 napi_create_reference(env, callback, 1, &callback_ref);
379 LockOptions options;
380 options.isAvailable = true;
381 lockPtr->LockAsync(env, callback_ref, LOCK_MODE_EXCLUSIVE, options);
382 data.end.arrive_and_wait();
383 LocksTest::DestroyEngine();
384 });
385 napi_env env = GetEnv();
386 napi_value callback = CreateFunction("isavailable", IsAvailableCb, &data);
387 napi_ref callback_ref;
388 napi_create_reference(env, callback, 1, &callback_ref);
389
390 LockOptions options;
391
392 lock->LockAsync(env, callback_ref, LOCK_MODE_EXCLUSIVE, options);
393 LoopUntil([&data] () { return data.callCount > 0; });
394 t.join();
395 ASSERT_EQ(data.callCount, 1U);
396 }
397
AsyncExclusiveCb(napi_env env,napi_callback_info info)398 static napi_value AsyncExclusiveCb(napi_env env, napi_callback_info info)
399 {
400 napi_value undefined;
401 napi_get_undefined(env, &undefined);
402
403 CallbackData *data;
404 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
405
406 data->callCount += 1;
407 bool prev = data->executing.exchange(true);
408 if (prev) {
409 data->fail = true;
410 return undefined;
411 }
412
413 napi_status status;
414
415 napi_value promise;
416 napi_deferred deferred;
417 status = napi_create_promise(env, &deferred, &promise);
418 EXPECT_EQ(status, napi_ok);
419
420 status = napi_resolve_deferred(env, deferred, undefined);
421 EXPECT_EQ(status, napi_ok);
422
423 LocksTest::Sleep();
424 data->executing = false;
425
426 return promise;
427 }
428
TEST_F(LocksTest,PendingRequestAfterEnvDestroyed)429 TEST_F(LocksTest, PendingRequestAfterEnvDestroyed)
430 {
431 CallbackData callbackData;
432
433 napi_env env {GetEnv()};
434 napi_value lockName;
435 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
436 napi_value requestArgs[1] {lockName};
437 napi_value lock;
438 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
439 napi_value lockAsync;
440 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
441 napi_value callback = CreateFunction("PendingRequestAfterEnvDestroyed", AsyncExclusiveCb, &callbackData);
442 napi_value args[1] {callback};
443 napi_value result;
444 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, 1, args, &result));
445 bool isPromise {false};
446 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
447 ASSERT_TRUE(isPromise);
448
449 std::thread t([&callbackData]() {
450 LocksTest::InitializeEngine();
451
452 napi_env env {GetEnv()};
453 napi_value lockName;
454 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
455 napi_value requestArgs[1] {lockName};
456 napi_value lock;
457 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
458 napi_value lockAsync;
459 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
460 napi_value callback = CreateFunction("PendingRequestAfterEnvDestroyed", AsyncExclusiveCb, &callbackData);
461 napi_value args[1] {callback};
462 napi_value result;
463 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, 1, args, &result));
464 bool isPromise {false};
465 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
466 ASSERT_TRUE(isPromise);
467
468 LocksTest::DestroyEngine();
469 });
470
471 t.join();
472 Loop(LOOP_ONCE);
473 ASSERT_EQ(callbackData.callCount, 1U);
474 ASSERT_FALSE(callbackData.fail);
475 }
476
AsyncSharedCb(napi_env env,napi_callback_info info)477 static napi_value AsyncSharedCb(napi_env env, napi_callback_info info)
478 {
479 napi_value undefined;
480 napi_get_undefined(env, &undefined);
481
482 CallbackData *data;
483 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
484
485 data->callCount += 1;
486 LocksTest::Sleep();
487
488 napi_status status;
489
490 napi_value promise;
491 napi_deferred deferred;
492 status = napi_create_promise(env, &deferred, &promise);
493 EXPECT_EQ(status, napi_ok);
494
495 status = napi_resolve_deferred(env, deferred, undefined);
496 EXPECT_EQ(status, napi_ok);
497
498 data->executing = false;
499 return promise;
500 }
501
TEST_F(LocksTest,SharedModeWithEnvDestroyed)502 TEST_F(LocksTest, SharedModeWithEnvDestroyed)
503 {
504 CallbackData callbackData;
505
506 napi_env env {GetEnv()};
507 napi_value lockName;
508 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
509 napi_value requestArgs[1] {lockName};
510 napi_value lock;
511 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
512 napi_value lockAsync;
513 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
514 napi_value callback = CreateFunction("SharedModeWithEnvDestroyed", AsyncSharedCb, &callbackData);
515 size_t argc {2};
516 napi_value args[2] {callback, sharedMode_};
517 napi_value result;
518 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
519 bool isPromise {false};
520 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
521 ASSERT_TRUE(isPromise);
522
523 std::thread t([&callbackData]() {
524 LocksTest::InitializeEngine();
525
526 napi_env env {GetEnv()};
527 napi_value lockName;
528 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
529 napi_value requestArgs[1] {lockName};
530 napi_value lock;
531 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
532 napi_value lockAsync;
533 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
534 napi_value callback = CreateFunction("SharedModeWithEnvDestroyed", AsyncSharedCb, &callbackData);
535 size_t argc {2};
536 napi_value args[2] {callback, sharedMode_};
537 napi_value result;
538 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
539 bool isPromise {false};
540 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
541 ASSERT_TRUE(isPromise);
542 Loop(LOOP_ONCE);
543
544 LocksTest::DestroyEngine();
545 });
546
547 Loop(LOOP_ONCE);
548 t.join();
549 ASSERT_EQ(callbackData.callCount, 2U);
550 }
551
AsyncTimeoutCb(napi_env env,napi_callback_info info)552 static napi_value AsyncTimeoutCb(napi_env env, napi_callback_info info)
553 {
554 napi_value undefined;
555 napi_get_undefined(env, &undefined);
556
557 IsAvailableCallbackData *data;
558 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
559 data->callCount += 1;
560 data->begin.arrive_and_wait();
561
562 napi_status status;
563
564 napi_value promise;
565 napi_deferred deferred;
566 status = napi_create_promise(env, &deferred, &promise);
567 EXPECT_EQ(status, napi_ok);
568
569 status = napi_resolve_deferred(env, deferred, undefined);
570 EXPECT_EQ(status, napi_ok);
571
572 data->end.arrive_and_wait();
573 return promise;
574 }
575
TEST_F(LocksTest,TimeoutLockWithEnvDestroyedTest)576 TEST_F(LocksTest, TimeoutLockWithEnvDestroyedTest)
577 {
578 std::latch begin(2U);
579 std::latch end(2U);
580 IsAvailableCallbackData callbackData(begin, end);
581
582 napi_env env {GetEnv()};
583 napi_value lockName;
584 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
585 napi_value requestArgs[1] {lockName};
586 napi_value lock;
587 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
588 napi_value lockAsync;
589 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
590 napi_value callback = CreateFunction("TimeoutLockWithEnvDestroyedTest", AsyncTimeoutCb, &callbackData);
591 napi_value args[1] {callback};
592 napi_value result;
593 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, 1, args, &result));
594
595 std::thread t([&callbackData]() {
596 LocksTest::InitializeEngine();
597 callbackData.begin.arrive_and_wait();
598
599 napi_env env {GetEnv()};
600 napi_value lockName;
601 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
602 napi_value requestArgs[1] {lockName};
603 napi_value lock;
604 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
605 napi_value lockAsync;
606 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
607 napi_value callback = CreateFunction("TimeoutLockWithEnvDestroyedTest", AsyncTimeoutCb, &callbackData);
608 napi_value options;
609 ASSERT_CHECK_CALL(napi_new_instance(env, asyncLockOptions_, 0, nullptr, &options));
610 napi_value timeout;
611 ASSERT_CHECK_CALL(napi_create_int32(env, defaultTimeout, &timeout));
612 ASSERT_CHECK_CALL(napi_set_named_property(env, options, "timeout", timeout));
613 size_t argc {3};
614 napi_value args[3] {callback, exclusiveMode_, options};
615 napi_value result;
616 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
617
618 Loop(LOOP_ONCE);
619 LocksTest::Sleep();
620 callbackData.end.arrive_and_wait();
621 LocksTest::DestroyEngine();
622 });
623
624 Loop(LOOP_ONCE);
625 t.join();
626 ASSERT_EQ(callbackData.callCount, 1U);
627 }
628
629 struct Defer {
630 napi_deferred deferred;
631 CallbackData *data;
632 };
633
AsyncCb(napi_env env,napi_callback_info info)634 static napi_value AsyncCb(napi_env env, napi_callback_info info)
635 {
636 napi_value undefined;
637 napi_get_undefined(env, &undefined);
638
639 CallbackData *data;
640 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
641
642 napi_status status;
643
644 napi_value promise;
645 auto defer {new Defer};
646 defer->data = data;
647 status = napi_create_promise(env, &defer->deferred, &promise);
648 EXPECT_EQ(status, napi_ok);
649
650 uv_loop_t *loop;
651 napi_get_uv_event_loop(env, &loop);
652 uv_timer_t *timer = new uv_timer_t;
653 uv_timer_init(loop, timer);
654 timer->data = defer;
655 uv_timer_start(
656 timer,
657 [](uv_timer_t *timer) {
658 Defer *defer = reinterpret_cast<Defer *>(timer->data);
659 napi_env env {LocksTest::GetEnv()};
660 LocksTest::Sleep();
661
662 napi_value undefined;
663 napi_get_undefined(env, &undefined);
664
665 defer->data->callCount += 1;
666
667 napi_status status = napi_resolve_deferred(env, defer->deferred, undefined);
668 EXPECT_EQ(status, napi_ok);
669
670 delete defer;
671 uv_close(reinterpret_cast<uv_handle_t *>(timer), [](uv_handle_t *timer) { delete timer; });
672 },
673 LocksTest::defaultTimeout, 0);
674
675 EXPECT_EQ(status, napi_ok);
676
677 data->executing = false;
678 return promise;
679 }
680
TEST_F(LocksTest,PendingSharedRequestAfterGC)681 TEST_F(LocksTest, PendingSharedRequestAfterGC)
682 {
683 CallbackData callbackData;
684 napi_env env {GetEnv()};
685 TriggerGC();
686 {
687 napi_handle_scope scope;
688 napi_open_handle_scope(env, &scope);
689
690 napi_value lockName;
691 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
692 napi_value requestArgs[1] {lockName};
693 napi_value lock;
694 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
695 napi_value lockAsync;
696 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
697 napi_value callback = CreateFunction("SharedModeWithEnvDestroyed", AsyncCb, &callbackData);
698 size_t argc {2};
699 napi_value args[2] {callback, sharedMode_};
700 napi_value result;
701 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
702
703 bool isPromise {false};
704 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
705 ASSERT_TRUE(isPromise);
706
707 napi_close_handle_scope(env, scope);
708 }
709 {
710 napi_handle_scope scope;
711 napi_open_handle_scope(env, &scope);
712
713 napi_value lockName;
714 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
715 napi_value requestArgs[1] {lockName};
716 napi_value lock;
717 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
718 napi_value lockAsync;
719 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
720 napi_value callback = CreateFunction("SharedModeWithEnvDestroyed", AsyncCb, &callbackData);
721 size_t argc {2};
722 napi_value args[2] {callback, sharedMode_};
723 napi_value result;
724 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
725
726 bool isPromise {false};
727 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
728 ASSERT_TRUE(isPromise);
729
730 napi_close_handle_scope(env, scope);
731 }
732 TriggerGC();
733
734 Loop(LOOP_ONCE);
735 Loop(LOOP_ONCE);
736 ASSERT_EQ(callbackData.callCount, 2U);
737 }
738