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 ASSERT_TRUE(isCalled);
261 }
262
263 struct SharedMultiCallbackData: public CallbackData {
SharedMultiCallbackDataSharedMultiCallbackData264 explicit SharedMultiCallbackData(std::latch &barrier): CallbackData(), barrier(barrier)
265 {
266 }
267
268 std::latch &barrier;
269 };
270
MainSharedLockMultiCb(napi_env env,napi_callback_info info)271 static napi_value MainSharedLockMultiCb(napi_env env, napi_callback_info info)
272 {
273 napi_value undefined;
274 napi_get_undefined(env, &undefined);
275 SharedMultiCallbackData *data = nullptr;
276 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
277 data->barrier.arrive_and_wait();
278 data->callCount += 1;
279
280 LocksTest::Sleep();
281 data->executing.exchange(true);
282
283 if (data->callCount != 2) {
284 data->fail = true;
285 return undefined;
286 }
287
288 data->executing = false;
289 return undefined;
290 }
291
SharedLockMultiCb(napi_env env,napi_callback_info info)292 static napi_value SharedLockMultiCb(napi_env env, napi_callback_info info)
293 {
294 napi_value undefined;
295 napi_get_undefined(env, &undefined);
296 CallbackData *data = nullptr;
297 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
298 data->callCount += 1;
299
300 LocksTest::Sleep();
301
302 if (data->callCount != 2) {
303 data->fail = true;
304 return undefined;
305 }
306
307 data->executing = false;
308 return undefined;
309 }
310
TEST_F(LocksTest,SharedLockMulti)311 TEST_F(LocksTest, SharedLockMulti)
312 {
313 std::unique_ptr<AsyncLock> lock = std::make_unique<AsyncLock>(1);
314 AsyncLock *lockPtr = lock.get();
315 std::latch barrier(2U);
316 SharedMultiCallbackData callbackData(barrier);
317 std::thread t([lockPtr, &callbackData, &barrier] () {
318 LocksTest::InitializeEngine();
319 napi_env env = GetEnv();
320 napi_value callback = CreateFunction("sharedlockmulti", SharedLockMultiCb, &callbackData);
321 napi_ref callback_ref;
322 napi_create_reference(env, callback, 1, &callback_ref);
323 LockOptions options;
324 barrier.arrive_and_wait();
325 lockPtr->LockAsync(env, callback_ref, LOCK_MODE_SHARED, options);
326 LocksTest::DestroyEngine();
327 });
328 napi_env env = GetEnv();
329 napi_value callback = CreateFunction("sharedlockmulti", MainSharedLockMultiCb, &callbackData);
330 napi_ref callback_ref;
331 napi_create_reference(env, callback, 1, &callback_ref);
332
333 LockOptions options;
334 lock->LockAsync(env, callback_ref, LOCK_MODE_SHARED, options);
335 t.join();
336 ASSERT_FALSE(callbackData.fail);
337 ASSERT_EQ(callbackData.callCount, 2U);
338 }
339
340 struct IsAvailableCallbackData {
IsAvailableCallbackDataIsAvailableCallbackData341 IsAvailableCallbackData(std::latch &b, std::latch &e): begin(b), end(e)
342 {
343 }
344
345 std::atomic<uint32_t> callCount = 0;
346 std::latch &begin;
347 std::latch &end;
348 };
349
IsAvailableCb(napi_env env,napi_callback_info info)350 static napi_value IsAvailableCb(napi_env env, napi_callback_info info)
351 {
352 IsAvailableCallbackData *data = nullptr;
353 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
354 data->callCount += 1;
355 data->begin.arrive_and_wait();
356 data->end.arrive_and_wait();
357 napi_value undefined;
358 napi_get_undefined(env, &undefined);
359 return undefined;
360 }
361
TEST_F(LocksTest,IsAvailable)362 TEST_F(LocksTest, IsAvailable)
363 {
364 std::unique_ptr<AsyncLock> lock = std::make_unique<AsyncLock>(1);
365 AsyncLock *lockPtr = lock.get();
366 std::latch begin(2U);
367 std::latch end(2U);
368 IsAvailableCallbackData data(begin, end);
369 std::thread t([lockPtr, &data] () {
370 LocksTest::InitializeEngine();
371 data.begin.arrive_and_wait();
372 napi_env env = GetEnv();
373 napi_value callback = CreateFunction("isavailable", IsAvailableCb, &data);
374 napi_ref callback_ref;
375 napi_create_reference(env, callback, 1, &callback_ref);
376 LockOptions options;
377 options.isAvailable = true;
378 lockPtr->LockAsync(env, callback_ref, LOCK_MODE_EXCLUSIVE, options);
379 data.end.arrive_and_wait();
380 LocksTest::DestroyEngine();
381 });
382 napi_env env = GetEnv();
383 napi_value callback = CreateFunction("isavailable", IsAvailableCb, &data);
384 napi_ref callback_ref;
385 napi_create_reference(env, callback, 1, &callback_ref);
386
387 LockOptions options;
388
389 lock->LockAsync(env, callback_ref, LOCK_MODE_EXCLUSIVE, options);
390 LoopUntil([&data] () { return data.callCount > 0; });
391 t.join();
392 ASSERT_EQ(data.callCount, 1U);
393 }
394
AsyncExclusiveCb(napi_env env,napi_callback_info info)395 static napi_value AsyncExclusiveCb(napi_env env, napi_callback_info info)
396 {
397 napi_value undefined;
398 napi_get_undefined(env, &undefined);
399
400 CallbackData *data;
401 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
402
403 data->callCount += 1;
404 bool prev = data->executing.exchange(true);
405 if (prev) {
406 data->fail = true;
407 return undefined;
408 }
409
410 napi_status status;
411
412 napi_value promise;
413 napi_deferred deferred;
414 status = napi_create_promise(env, &deferred, &promise);
415 EXPECT_EQ(status, napi_ok);
416
417 status = napi_resolve_deferred(env, deferred, undefined);
418 EXPECT_EQ(status, napi_ok);
419
420 LocksTest::Sleep();
421 data->executing = false;
422
423 return promise;
424 }
425
TEST_F(LocksTest,PendingRequestAfterEnvDestroyed)426 TEST_F(LocksTest, PendingRequestAfterEnvDestroyed)
427 {
428 CallbackData callbackData;
429
430 napi_env env {GetEnv()};
431 napi_value lockName;
432 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
433 napi_value requestArgs[1] {lockName};
434 napi_value lock;
435 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
436 napi_value lockAsync;
437 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
438 napi_value callback = CreateFunction("PendingRequestAfterEnvDestroyed", AsyncExclusiveCb, &callbackData);
439 napi_value args[1] {callback};
440 napi_value result;
441 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, 1, args, &result));
442 bool isPromise {false};
443 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
444 ASSERT_TRUE(isPromise);
445
446 std::thread t([&callbackData]() {
447 LocksTest::InitializeEngine();
448
449 napi_env env {GetEnv()};
450 napi_value lockName;
451 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
452 napi_value requestArgs[1] {lockName};
453 napi_value lock;
454 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
455 napi_value lockAsync;
456 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
457 napi_value callback = CreateFunction("PendingRequestAfterEnvDestroyed", AsyncExclusiveCb, &callbackData);
458 napi_value args[1] {callback};
459 napi_value result;
460 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, 1, args, &result));
461 bool isPromise {false};
462 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
463 ASSERT_TRUE(isPromise);
464
465 LocksTest::DestroyEngine();
466 });
467
468 t.join();
469 Loop(LOOP_ONCE);
470 ASSERT_EQ(callbackData.callCount, 1U);
471 ASSERT_FALSE(callbackData.fail);
472 }
473
AsyncSharedCb(napi_env env,napi_callback_info info)474 static napi_value AsyncSharedCb(napi_env env, napi_callback_info info)
475 {
476 napi_value undefined;
477 napi_get_undefined(env, &undefined);
478
479 CallbackData *data;
480 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
481
482 data->callCount += 1;
483 LocksTest::Sleep();
484
485 napi_status status;
486
487 napi_value promise;
488 napi_deferred deferred;
489 status = napi_create_promise(env, &deferred, &promise);
490 EXPECT_EQ(status, napi_ok);
491
492 status = napi_resolve_deferred(env, deferred, undefined);
493 EXPECT_EQ(status, napi_ok);
494
495 data->executing = false;
496 return promise;
497 }
498
TEST_F(LocksTest,SharedModeWithEnvDestroyed)499 TEST_F(LocksTest, SharedModeWithEnvDestroyed)
500 {
501 CallbackData callbackData;
502
503 napi_env env {GetEnv()};
504 napi_value lockName;
505 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
506 napi_value requestArgs[1] {lockName};
507 napi_value lock;
508 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
509 napi_value lockAsync;
510 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
511 napi_value callback = CreateFunction("SharedModeWithEnvDestroyed", AsyncSharedCb, &callbackData);
512 size_t argc {2};
513 napi_value args[2] {callback, sharedMode_};
514 napi_value result;
515 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
516 bool isPromise {false};
517 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
518 ASSERT_TRUE(isPromise);
519
520 std::thread t([&callbackData]() {
521 LocksTest::InitializeEngine();
522
523 napi_env env {GetEnv()};
524 napi_value lockName;
525 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
526 napi_value requestArgs[1] {lockName};
527 napi_value lock;
528 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
529 napi_value lockAsync;
530 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
531 napi_value callback = CreateFunction("SharedModeWithEnvDestroyed", AsyncSharedCb, &callbackData);
532 size_t argc {2};
533 napi_value args[2] {callback, sharedMode_};
534 napi_value result;
535 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
536 bool isPromise {false};
537 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
538 ASSERT_TRUE(isPromise);
539
540 LocksTest::DestroyEngine();
541 });
542
543 t.join();
544 ASSERT_EQ(callbackData.callCount, 2U);
545 }
546
AsyncTimeoutCb(napi_env env,napi_callback_info info)547 static napi_value AsyncTimeoutCb(napi_env env, napi_callback_info info)
548 {
549 napi_value undefined;
550 napi_get_undefined(env, &undefined);
551
552 IsAvailableCallbackData *data;
553 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
554 data->callCount += 1;
555 data->begin.arrive_and_wait();
556
557 napi_status status;
558
559 napi_value promise;
560 napi_deferred deferred;
561 status = napi_create_promise(env, &deferred, &promise);
562 EXPECT_EQ(status, napi_ok);
563
564 status = napi_resolve_deferred(env, deferred, undefined);
565 EXPECT_EQ(status, napi_ok);
566
567 data->end.arrive_and_wait();
568 return promise;
569 }
570
TEST_F(LocksTest,TimeoutLockWithEnvDestroyedTest)571 TEST_F(LocksTest, TimeoutLockWithEnvDestroyedTest)
572 {
573 std::latch begin(2U);
574 std::latch end(2U);
575 IsAvailableCallbackData callbackData(begin, end);
576
577 napi_env env {GetEnv()};
578 napi_value lockName;
579 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
580 napi_value requestArgs[1] {lockName};
581 napi_value lock;
582 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
583 napi_value lockAsync;
584 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
585 napi_value callback = CreateFunction("TimeoutLockWithEnvDestroyedTest", AsyncTimeoutCb, &callbackData);
586 napi_value args[1] {callback};
587 napi_value result;
588 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, 1, args, &result));
589
590 std::thread t([&callbackData]() {
591 LocksTest::InitializeEngine();
592 callbackData.begin.arrive_and_wait();
593
594 napi_env env {GetEnv()};
595 napi_value lockName;
596 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
597 napi_value requestArgs[1] {lockName};
598 napi_value lock;
599 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
600 napi_value lockAsync;
601 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
602 napi_value callback = CreateFunction("TimeoutLockWithEnvDestroyedTest", AsyncTimeoutCb, &callbackData);
603 napi_value options;
604 ASSERT_CHECK_CALL(napi_new_instance(env, asyncLockOptions_, 0, nullptr, &options));
605 napi_value timeout;
606 ASSERT_CHECK_CALL(napi_create_int32(env, defaultTimeout, &timeout));
607 ASSERT_CHECK_CALL(napi_set_named_property(env, options, "timeout", timeout));
608 size_t argc {3};
609 napi_value args[3] {callback, exclusiveMode_, options};
610 napi_value result;
611 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
612
613 LocksTest::Sleep();
614 callbackData.end.arrive_and_wait();
615 LocksTest::DestroyEngine();
616 });
617
618 Loop(LOOP_ONCE);
619 t.join();
620 ASSERT_EQ(callbackData.callCount, 1U);
621 }
622
623 struct Defer {
624 napi_deferred deferred;
625 CallbackData *data;
626 };
627
AsyncCb(napi_env env,napi_callback_info info)628 static napi_value AsyncCb(napi_env env, napi_callback_info info)
629 {
630 napi_value undefined;
631 napi_get_undefined(env, &undefined);
632
633 CallbackData *data;
634 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&data));
635
636 napi_status status;
637
638 napi_value promise;
639 auto defer {new Defer};
640 defer->data = data;
641 status = napi_create_promise(env, &defer->deferred, &promise);
642 EXPECT_EQ(status, napi_ok);
643
644 uv_loop_t *loop;
645 napi_get_uv_event_loop(env, &loop);
646 uv_timer_t *timer = new uv_timer_t;
647 uv_timer_init(loop, timer);
648 timer->data = defer;
649 uv_timer_start(
650 timer,
651 [](uv_timer_t *timer) {
652 Defer *defer = reinterpret_cast<Defer *>(timer->data);
653 napi_env env {LocksTest::GetEnv()};
654 LocksTest::Sleep();
655
656 napi_value undefined;
657 napi_get_undefined(env, &undefined);
658
659 defer->data->callCount += 1;
660
661 napi_status status = napi_resolve_deferred(env, defer->deferred, undefined);
662 EXPECT_EQ(status, napi_ok);
663
664 delete defer;
665 uv_close(reinterpret_cast<uv_handle_t *>(timer), [](uv_handle_t *timer) { delete timer; });
666 },
667 LocksTest::defaultTimeout, 0);
668
669 EXPECT_EQ(status, napi_ok);
670
671 data->executing = false;
672 return promise;
673 }
674
TEST_F(LocksTest,PendingSharedRequestAfterGC)675 TEST_F(LocksTest, PendingSharedRequestAfterGC)
676 {
677 CallbackData callbackData;
678 napi_env env {GetEnv()};
679 TriggerGC();
680 {
681 napi_handle_scope scope;
682 napi_open_handle_scope(env, &scope);
683
684 napi_value lockName;
685 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
686 napi_value requestArgs[1] {lockName};
687 napi_value lock;
688 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
689 napi_value lockAsync;
690 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
691 napi_value callback = CreateFunction("SharedModeWithEnvDestroyed", AsyncCb, &callbackData);
692 size_t argc {2};
693 napi_value args[2] {callback, sharedMode_};
694 napi_value result;
695 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
696
697 bool isPromise {false};
698 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
699 ASSERT_TRUE(isPromise);
700
701 napi_close_handle_scope(env, scope);
702 }
703 {
704 napi_handle_scope scope;
705 napi_open_handle_scope(env, &scope);
706
707 napi_value lockName;
708 ASSERT_CHECK_CALL(napi_create_string_utf8(env, "lockName", NAPI_AUTO_LENGTH, &lockName));
709 napi_value requestArgs[1] {lockName};
710 napi_value lock;
711 ASSERT_CHECK_CALL(napi_call_function(env, undefined_, asyncLockRequest_, 1, requestArgs, &lock));
712 napi_value lockAsync;
713 ASSERT_CHECK_CALL(napi_get_named_property(env, lock, "lockAsync", &lockAsync));
714 napi_value callback = CreateFunction("SharedModeWithEnvDestroyed", AsyncCb, &callbackData);
715 size_t argc {2};
716 napi_value args[2] {callback, sharedMode_};
717 napi_value result;
718 ASSERT_CHECK_CALL(napi_call_function(env, lock, lockAsync, argc, args, &result));
719
720 bool isPromise {false};
721 ASSERT_CHECK_CALL(napi_is_promise(env, result, &isPromise));
722 ASSERT_TRUE(isPromise);
723
724 napi_close_handle_scope(env, scope);
725 }
726 TriggerGC();
727
728 Loop(LOOP_ONCE);
729 ASSERT_EQ(callbackData.callCount, 2U);
730 }
731