• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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