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