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