• 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 
16 #include <gtest/gtest.h>
17 #include <array>
18 
19 #include "libpandabase/os/mutex.h"
20 #include "runtime/include/thread_scopes.h"
21 #include "runtime/include/gc_task.h"
22 #include "runtime/tests/test_utils.h"
23 #include "plugins/ets/runtime/ets_vm.h"
24 #include "plugins/ets/runtime/ets_coroutine.h"
25 #include "plugins/ets/runtime/ets_handle.h"
26 #include "plugins/ets/runtime/ets_handle_scope.h"
27 #include "plugins/ets/runtime/types/ets_class.h"
28 #include "plugins/ets/runtime/types/ets_array.h"
29 #include "plugins/ets/runtime/types/ets_finalizable_weak_ref.h"
30 #include "plugins/ets/tests/runtime/types/ets_test_mirror_classes.h"
31 
32 namespace ark::ets::test {
33 
34 class EtsFinalizableWeakRefTest : public testing::Test {
35 public:
EtsFinalizableWeakRefTest()36     EtsFinalizableWeakRefTest()
37     {
38         RuntimeOptions options;
39         options.SetShouldLoadBootPandaFiles(true);
40         options.SetShouldInitializeIntrinsics(false);
41         options.SetCompilerEnableJit(false);
42         options.SetGcType("g1-gc");
43         options.SetLoadRuntimes({"ets"});
44         options.SetUseTlabForAllocations(false);
45         options.SetGcTriggerType("debug");
46         options.SetGcDebugTriggerStart(std::numeric_limits<int>::max());
47         options.SetExplicitConcurrentGcEnabled(false);
48 
49         auto stdlib = std::getenv("PANDA_STD_LIB");
50         if (stdlib == nullptr) {
51             std::cerr << "PANDA_STD_LIB env variable should be set and point to mock_stdlib.abc" << std::endl;
52             std::abort();
53         }
54         options.SetBootPandaFiles({stdlib});
55 
56         Runtime::Create(options);
57         EtsCoroutine *coroutine = EtsCoroutine::GetCurrent();
58         vm_ = coroutine->GetPandaVM();
59     }
60 
~EtsFinalizableWeakRefTest()61     ~EtsFinalizableWeakRefTest() override
62     {
63         Runtime::Destroy();
64     }
65 
66     NO_COPY_SEMANTIC(EtsFinalizableWeakRefTest);
67     NO_MOVE_SEMANTIC(EtsFinalizableWeakRefTest);
68 
CompareMemberOffsets(EtsClass * klass,const std::vector<MirrorFieldInfo> & members)69     static void CompareMemberOffsets(EtsClass *klass, const std::vector<MirrorFieldInfo> &members)
70     {
71         ASSERT_NE(nullptr, klass);
72         ASSERT_EQ(members.size(), klass->GetInstanceFieldsNumber());
73 
74         // Check both EtsPromise and ark::Class<Promise> has the same number of fields
75         // and at the same offsets
76         for (const MirrorFieldInfo &memb : members) {
77             EtsField *field = klass->GetFieldIDByName(memb.Name());
78             ASSERT_NE(nullptr, field);
79             ASSERT_EQ(memb.Offset(), field->GetOffset())
80                 << "Offsets of the field '" << memb.Name() << "' are different";
81         }
82     }
83 
GetFinalizableWeakRefMembers()84     static std::vector<MirrorFieldInfo> GetFinalizableWeakRefMembers()
85     {
86         return std::vector<MirrorFieldInfo> {
87             MIRROR_FIELD_INFO(EtsFinalizableWeakRef, referent_, "referent"),
88             MIRROR_FIELD_INFO(EtsFinalizableWeakRef, prev_, "prevRef"),
89             MIRROR_FIELD_INFO(EtsFinalizableWeakRef, next_, "nextRef"),
90             MIRROR_FIELD_INFO(EtsFinalizableWeakRef, finalizerPtr_, "finalizerPtr"),
91             MIRROR_FIELD_INFO(EtsFinalizableWeakRef, finalizerArgPtr_, "finalizerArgPtr")};
92     }
93 
AllocObjectInYoung()94     static ObjectHeader *AllocObjectInYoung()
95     {
96         auto ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
97         return reinterpret_cast<ObjectHeader *>(
98             coretypes::String::CreateEmptyString(ctx, Runtime::GetCurrent()->GetPandaVM()));
99     }
100 
TriggerGC()101     static void TriggerGC()
102     {
103         auto *coro = EtsCoroutine::GetCurrent();
104         auto *obj = AllocObjectInYoung();
105         EtsHandleScope arrayScope(coro);
106         VMHandle<ObjectHeader> hObj(EtsCoroutine::GetCurrent(), obj);
107         while (hObj.GetPtr() == obj) {
108             AllocObjectInYoung();
109         }
110     }
111 
112 protected:
113     PandaEtsVM *vm_ = nullptr;  // NOLINT(misc-non-private-member-variables-in-classes)
114 };
115 
TEST_F(EtsFinalizableWeakRefTest,FinalizableWeakRefMemoryLayout)116 TEST_F(EtsFinalizableWeakRefTest, FinalizableWeakRefMemoryLayout)
117 {
118     EtsClass *finalizableWeakRefClass = vm_->GetClassLinker()->GetFinalizableWeakRefClass();
119     CompareMemberOffsets(finalizableWeakRefClass, GetFinalizableWeakRefMembers());
120 }
121 
122 template <size_t EVENT_COUNT>
123 class TestEvent {
124 public:
Finalize(void * arg)125     static void Finalize(void *arg)
126     {
127         auto *finalizer = reinterpret_cast<TestEvent *>(arg);
128         finalizer->Fire();
129     }
130 
IsHappened() const131     bool IsHappened() const
132     {
133         return happened_;
134     }
135 
Wait()136     void Wait()
137     {
138         os::memory::LockHolder lh(mutex_);
139         while (!happened_) {
140             cv_.Wait(&mutex_);
141         }
142     }
143 
Fire()144     void Fire()
145     {
146         os::memory::LockHolder lh(mutex_);
147         if (++eventCount_ == EVENT_COUNT) {
148             happened_ = true;
149             cv_.Signal();
150         }
151     }
152 
153 private:
154     size_t eventCount_ = 0;
155     std::atomic<bool> happened_ = false;
156     os::memory::Mutex mutex_;
157     os::memory::ConditionVariable cv_;
158 };
159 
TEST_F(EtsFinalizableWeakRefTest,RegisterFinalizerTest)160 TEST_F(EtsFinalizableWeakRefTest, RegisterFinalizerTest)
161 {
162     // Initialize
163     auto *coro = EtsCoroutine::GetCurrent();
164     ScopedManagedCodeThread managedScope(coro);
165     static constexpr size_t EVENT_COUNT = 10U;
166     TestEvent<EVENT_COUNT> event;
167     // Create referents
168     {
169         auto *objectClass = vm_->GetClassLinker()->GetObjectClass();
170         static constexpr size_t ARRAY_SIZE = 1024U;
171         EtsHandleScope arrayScope(coro);
172         std::array<EtsHandle<EtsObject>, EVENT_COUNT> arrayHandles;
173         for (auto &handle : arrayHandles) {
174             handle = EtsHandle<EtsObject>(coro, EtsObjectArray::Create(objectClass, ARRAY_SIZE)->AsObject());
175             vm_->RegisterFinalizerForObject(coro, handle, TestEvent<EVENT_COUNT>::Finalize, &event);
176         }
177 
178         // Trigger GC
179         TriggerGC();
180 
181         // Check finalizer was not called because referent is alive
182         ASSERT(!event.IsHappened());
183     }
184     // Referents are dead
185 
186     // Trigger Full GC to handle references
187     vm_->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::OOM_CAUSE));
188 
189     // Check that finalizer was called
190     event.Wait();
191     ASSERT(event.IsHappened());
192 }
193 
194 }  // namespace ark::ets::test
195