1 /**
2 * Copyright (c) 2021-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 <ctime>
17
18 #include "gtest/gtest.h"
19 #include "runtime/include/runtime.h"
20 #include "runtime/handle_base-inl.h"
21
22 namespace ark::concurrency::test {
23
24 class MonitorTest : public testing::Test {
25 public:
MonitorTest()26 MonitorTest()
27 {
28 // Logger::InitializeStdLogging(Logger::Level::DEBUG, Logger::Component::ALL);
29 #ifdef PANDA_NIGHTLY_TEST_ON
30 seed_ = std::time(NULL);
31 #else
32 // NOLINTNEXTLINE(readability-magic-numbers)
33 seed_ = 0xDEADBEEF;
34 #endif
35 srand(seed_);
36 // We need to create a runtime instance to be able to create strings.
37 options_.SetShouldLoadBootPandaFiles(false);
38 options_.SetShouldInitializeIntrinsics(false);
39 Runtime::Create(options_);
40 thread_ = ark::MTManagedThread::GetCurrent();
41 thread_->ManagedCodeBegin();
42 }
43
~MonitorTest()44 ~MonitorTest() override
45 {
46 thread_->ManagedCodeEnd();
47 Runtime::Destroy();
48 // Logger::Destroy();
49 }
50
51 NO_COPY_SEMANTIC(MonitorTest);
52 NO_MOVE_SEMANTIC(MonitorTest);
53
54 protected:
55 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
56 ark::MTManagedThread *thread_ {};
57
58 private:
59 unsigned seed_ {};
60 RuntimeOptions options_;
61 };
62
TEST_F(MonitorTest,MonitorEnterTest)63 TEST_F(MonitorTest, MonitorEnterTest)
64 {
65 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
66 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
67 auto header = ObjectHeader::Create(cls);
68 Monitor::MonitorEnter(header);
69 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
70 Monitor::MonitorExit(header);
71 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_UNLOCKED);
72 }
73
TEST_F(MonitorTest,MonitorDoubleEnterTest)74 TEST_F(MonitorTest, MonitorDoubleEnterTest)
75 {
76 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
77 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
78 auto header = ObjectHeader::Create(cls);
79 Monitor::MonitorEnter(header);
80 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
81 Monitor::MonitorEnter(header);
82 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
83 Monitor::MonitorExit(header);
84 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
85 Monitor::MonitorExit(header);
86 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_UNLOCKED);
87 }
88
TEST_F(MonitorTest,MonitorDoubleObjectTest)89 TEST_F(MonitorTest, MonitorDoubleObjectTest)
90 {
91 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
92 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
93 auto header1 = ObjectHeader::Create(cls);
94 auto header2 = ObjectHeader::Create(cls);
95 Monitor::MonitorEnter(header1);
96 ASSERT_TRUE(header1->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
97 ASSERT_TRUE(header2->AtomicGetMark().GetState() == MarkWord::STATE_UNLOCKED);
98 Monitor::MonitorEnter(header2);
99 ASSERT_TRUE(header1->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
100 ASSERT_TRUE(header2->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
101 Monitor::MonitorExit(header1);
102 ASSERT_TRUE(header1->AtomicGetMark().GetState() == MarkWord::STATE_UNLOCKED);
103 ASSERT_TRUE(header2->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
104 Monitor::MonitorExit(header2);
105 ASSERT_TRUE(header1->AtomicGetMark().GetState() == MarkWord::STATE_UNLOCKED);
106 ASSERT_TRUE(header2->AtomicGetMark().GetState() == MarkWord::STATE_UNLOCKED);
107 }
108
TEST_F(MonitorTest,HeavyMonitorEnterTest)109 TEST_F(MonitorTest, HeavyMonitorEnterTest)
110 {
111 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
112 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
113 auto header = ObjectHeader::Create(cls);
114 auto thread = MTManagedThread::GetCurrent();
115 Monitor::MonitorEnter(header);
116 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
117 ASSERT_TRUE(Monitor::Inflate(header, thread));
118 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
119 Monitor::MonitorExit(header);
120 // We unlock the monitor, but keep the pointer to it
121 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
122 ASSERT_FALSE(Monitor::HoldsLock(header));
123 }
124
TEST_F(MonitorTest,HeavyMonitorDeflateTest)125 TEST_F(MonitorTest, HeavyMonitorDeflateTest)
126 {
127 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
128 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
129 auto header = ObjectHeader::Create(cls);
130 auto thread = MTManagedThread::GetCurrent();
131 ASSERT_TRUE(Monitor::Inflate(header, thread));
132 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
133 Monitor::MonitorExit(header);
134 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
135 ASSERT_TRUE(Monitor::Deflate(header));
136 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_UNLOCKED);
137 }
138
TEST_F(MonitorTest,HeavyMonitorDoubleEnterTest)139 TEST_F(MonitorTest, HeavyMonitorDoubleEnterTest)
140 {
141 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
142 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
143 auto header = ObjectHeader::Create(cls);
144 auto thread = MTManagedThread::GetCurrent();
145 Monitor::MonitorEnter(header);
146 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
147 ASSERT_TRUE(Monitor::Inflate(header, thread));
148 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
149 Monitor::MonitorEnter(header);
150 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
151 Monitor::MonitorExit(header);
152 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
153 Monitor::MonitorExit(header);
154 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
155 ASSERT_FALSE(Monitor::HoldsLock(header));
156 }
157
TEST_F(MonitorTest,HeavyMonitorDoubleObjectTest)158 TEST_F(MonitorTest, HeavyMonitorDoubleObjectTest)
159 {
160 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
161 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
162 auto header1 = ObjectHeader::Create(cls);
163 auto header2 = ObjectHeader::Create(cls);
164 auto thread = MTManagedThread::GetCurrent();
165 ASSERT_TRUE(Monitor::Inflate(header1, thread));
166 ASSERT_TRUE(header1->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
167 ASSERT_TRUE(header2->AtomicGetMark().GetState() == MarkWord::STATE_UNLOCKED);
168 ASSERT_TRUE(Monitor::Inflate(header2, thread));
169 ASSERT_TRUE(header1->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
170 ASSERT_TRUE(header2->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
171 Monitor::MonitorExit(header1);
172 ASSERT_TRUE(header1->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
173 ASSERT_TRUE(header2->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
174 Monitor::MonitorExit(header2);
175 ASSERT_TRUE(header1->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
176 ASSERT_TRUE(header2->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
177 ASSERT_FALSE(Monitor::HoldsLock(header1));
178 ASSERT_FALSE(Monitor::HoldsLock(header2));
179 }
180
TEST_F(MonitorTest,MonitorDoubleObjectHoldsLockTest)181 TEST_F(MonitorTest, MonitorDoubleObjectHoldsLockTest)
182 {
183 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
184 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
185 auto header1 = ObjectHeader::Create(cls);
186 auto header2 = ObjectHeader::Create(cls);
187 ASSERT_FALSE(Monitor::HoldsLock(header1));
188 ASSERT_FALSE(Monitor::HoldsLock(header2));
189 Monitor::MonitorEnter(header1);
190 ASSERT_TRUE(Monitor::HoldsLock(header1));
191 ASSERT_FALSE(Monitor::HoldsLock(header2));
192 Monitor::MonitorEnter(header2);
193 ASSERT_TRUE(Monitor::HoldsLock(header1));
194 ASSERT_TRUE(Monitor::HoldsLock(header2));
195 Monitor::MonitorExit(header1);
196 ASSERT_FALSE(Monitor::HoldsLock(header1));
197 ASSERT_TRUE(Monitor::HoldsLock(header2));
198 Monitor::MonitorExit(header2);
199 ASSERT_FALSE(Monitor::HoldsLock(header1));
200 ASSERT_FALSE(Monitor::HoldsLock(header2));
201 }
202
TEST_F(MonitorTest,MonitorGenerateHashAndEnterTest)203 TEST_F(MonitorTest, MonitorGenerateHashAndEnterTest)
204 {
205 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
206 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
207 auto header = ObjectHeader::Create(cls);
208 auto hash = header->GetHashCode<PandaAssemblyLanguageConfig::MT_MODE>();
209 Monitor::MonitorEnter(header);
210 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
211 Monitor::MonitorExit(header);
212 // We unlock the monitor, but keep the pointer to it
213 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
214 ASSERT_TRUE(header->GetHashCode<PandaAssemblyLanguageConfig::MT_MODE>() == hash);
215 ASSERT_FALSE(Monitor::HoldsLock(header));
216 }
217
TEST_F(MonitorTest,MonitorEnterAndGenerateHashTest)218 TEST_F(MonitorTest, MonitorEnterAndGenerateHashTest)
219 {
220 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
221 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
222 auto header = ObjectHeader::Create(cls);
223 Monitor::MonitorEnter(header);
224 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
225 auto hash = header->GetHashCode<PandaAssemblyLanguageConfig::MT_MODE>();
226 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
227 ASSERT_TRUE(header->GetHashCode<PandaAssemblyLanguageConfig::MT_MODE>() == hash);
228 Monitor::MonitorExit(header);
229 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
230 ASSERT_TRUE(header->GetHashCode<PandaAssemblyLanguageConfig::MT_MODE>() == hash);
231 ASSERT_FALSE(Monitor::HoldsLock(header));
232 }
233
TEST_F(MonitorTest,HeavyMonitorGcTest)234 TEST_F(MonitorTest, HeavyMonitorGcTest)
235 {
236 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
237 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
238 auto thread = MTManagedThread::GetCurrent();
239 auto header = ObjectHeader::Create(cls);
240 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
241 VMHandle<ObjectHeader> objHandle(thread, header);
242 Monitor::MonitorEnter(objHandle.GetPtr());
243 ASSERT_TRUE(objHandle->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
244 ASSERT_TRUE(Monitor::Inflate(objHandle.GetPtr(), thread));
245 ASSERT_TRUE(objHandle->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
246 thread_->GetVM()->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE));
247 ASSERT_TRUE(objHandle->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
248 Monitor::MonitorExit(objHandle.GetPtr());
249 ASSERT_TRUE(objHandle->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
250 thread_->GetVM()->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE));
251 ASSERT_TRUE(objHandle->AtomicGetMark().GetState() == MarkWord::STATE_UNLOCKED);
252 ASSERT_FALSE(Monitor::HoldsLock(objHandle.GetPtr()));
253 }
254
TEST_F(MonitorTest,MonitorTestLightLockOverflow)255 TEST_F(MonitorTest, MonitorTestLightLockOverflow)
256 {
257 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
258 Class *cls = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
259 auto header = ObjectHeader::Create(cls);
260 Monitor::MonitorEnter(header);
261 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_LIGHT_LOCKED);
262 // Set lock count to MAX-1
263 {
264 MarkWord mark = header->AtomicGetMark();
265 MarkWord newMark = mark.DecodeFromLightLock(mark.GetThreadId(), MarkWord::LIGHT_LOCK_LOCK_MAX_COUNT - 1);
266 ASSERT_TRUE(header->AtomicSetMark(mark, newMark));
267 }
268 Monitor::MonitorEnter(header);
269 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
270 // Unlock all recursive locks
271 for (uint64_t cnt = 0; cnt < MarkWord::LIGHT_LOCK_LOCK_MAX_COUNT; cnt++) {
272 Monitor::MonitorExit(header);
273 }
274 ASSERT_TRUE(header->AtomicGetMark().GetState() == MarkWord::STATE_HEAVY_LOCKED);
275 ASSERT_FALSE(Monitor::HoldsLock(header));
276 }
277
278 } // namespace ark::concurrency::test
279