ART: Add art_quick_unlock_object to stub_test
Change-Id: Iaa8143fdc0144f5fa5e5c304995e2d7a2e0c91ca
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 8fbca94..5d9ad2c 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -182,6 +182,7 @@
#endif
}
+static constexpr size_t kThinLockLoops = 100;
#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
extern "C" void art_quick_lock_object(void);
@@ -206,6 +207,70 @@
LockWord lock_after = obj->GetLockWord(false);
LockWord::LockState new_state = lock_after.GetState();
EXPECT_EQ(LockWord::LockState::kThinLocked, new_state);
+ EXPECT_EQ(lock_after.ThinLockCount(), 0U); // Thin lock starts count at zero
+
+ for (size_t i = 1; i < kThinLockLoops; ++i) {
+ Invoke3(reinterpret_cast<size_t>(obj.get()), 0U, 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
+
+ // Check we're at lock count i
+
+ LockWord l_inc = obj->GetLockWord(false);
+ LockWord::LockState l_inc_state = l_inc.GetState();
+ EXPECT_EQ(LockWord::LockState::kThinLocked, l_inc_state);
+ EXPECT_EQ(l_inc.ThinLockCount(), i);
+ }
+
+ // TODO: Improve this test. Somehow force it to go to fat locked. But that needs another thread.
+
+#else
+ LOG(INFO) << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+class RandGen {
+ public:
+ explicit RandGen(uint32_t seed) : val_(seed) {}
+
+ uint32_t next() {
+ val_ = val_ * 48271 % 2147483647 + 13;
+ return val_;
+ }
+
+ uint32_t val_;
+};
+
+
+#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
+extern "C" void art_quick_lock_object(void);
+extern "C" void art_quick_unlock_object(void);
+#endif
+
+TEST_F(StubTest, UnlockObject) {
+#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
+ Thread* self = Thread::Current();
+ // Create an object
+ ScopedObjectAccess soa(self);
+ // garbage is created during ClassLinker::Init
+
+ SirtRef<mirror::String> obj(soa.Self(),
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!"));
+ LockWord lock = obj->GetLockWord(false);
+ LockWord::LockState old_state = lock.GetState();
+ EXPECT_EQ(LockWord::LockState::kUnlocked, old_state);
+
+ Invoke3(reinterpret_cast<size_t>(obj.get()), 0U, 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_unlock_object), self);
+
+ // This should be an illegal monitor state.
+ EXPECT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+
+ LockWord lock_after = obj->GetLockWord(false);
+ LockWord::LockState new_state = lock_after.GetState();
+ EXPECT_EQ(LockWord::LockState::kUnlocked, new_state);
Invoke3(reinterpret_cast<size_t>(obj.get()), 0U, 0U,
reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
@@ -214,12 +279,94 @@
LockWord::LockState new_state2 = lock_after2.GetState();
EXPECT_EQ(LockWord::LockState::kThinLocked, new_state2);
+ Invoke3(reinterpret_cast<size_t>(obj.get()), 0U, 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_unlock_object), self);
+
+ LockWord lock_after3 = obj->GetLockWord(false);
+ LockWord::LockState new_state3 = lock_after3.GetState();
+ EXPECT_EQ(LockWord::LockState::kUnlocked, new_state3);
+
+ // Stress test:
+ // Keep a number of objects and their locks in flight. Randomly lock or unlock one of them in
+ // each step.
+
+ RandGen r(0x1234);
+
+ constexpr size_t kNumberOfLocks = 10; // Number of objects = lock
+ constexpr size_t kIterations = 10000; // Number of iterations
+
+ size_t counts[kNumberOfLocks];
+ SirtRef<mirror::String>* objects[kNumberOfLocks];
+
+ // Initialize = allocate.
+ for (size_t i = 0; i < kNumberOfLocks; ++i) {
+ counts[i] = 0;
+ objects[i] = new SirtRef<mirror::String>(soa.Self(),
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), ""));
+ }
+
+ for (size_t i = 0; i < kIterations; ++i) {
+ // Select which lock to update.
+ size_t index = r.next() % kNumberOfLocks;
+
+ bool lock; // Whether to lock or unlock in this step.
+ if (counts[index] == 0) {
+ lock = true;
+ } else if (counts[index] == kThinLockLoops) {
+ lock = false;
+ } else {
+ // Randomly.
+ lock = r.next() % 2 == 0;
+ }
+
+ if (lock) {
+ Invoke3(reinterpret_cast<size_t>(objects[index]->get()), 0U, 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
+ counts[index]++;
+ } else {
+ Invoke3(reinterpret_cast<size_t>(objects[index]->get()), 0U, 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_unlock_object), self);
+ counts[index]--;
+ }
+
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ // Check the new state.
+ LockWord lock_iter = objects[index]->get()->GetLockWord(false);
+ LockWord::LockState iter_state = lock_iter.GetState();
+ if (counts[index] > 0) {
+ EXPECT_EQ(LockWord::LockState::kThinLocked, iter_state);
+ EXPECT_EQ(counts[index] - 1, lock_iter.ThinLockCount());
+ } else {
+ EXPECT_EQ(LockWord::LockState::kUnlocked, iter_state);
+ }
+ }
+
+ // Unlock the remaining count times and then check it's unlocked. Then deallocate.
+ // Go reverse order to correctly handle SirtRefs.
+ for (size_t i = 0; i < kNumberOfLocks; ++i) {
+ size_t index = kNumberOfLocks - 1 - i;
+ size_t count = counts[index];
+ while (count > 0) {
+ Invoke3(reinterpret_cast<size_t>(objects[index]->get()), 0U, 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_unlock_object), self);
+
+ count--;
+ }
+
+ LockWord lock_after4 = objects[index]->get()->GetLockWord(false);
+ LockWord::LockState new_state4 = lock_after4.GetState();
+ EXPECT_EQ(LockWord::LockState::kUnlocked, new_state4);
+
+ delete objects[index];
+ }
+
// TODO: Improve this test. Somehow force it to go to fat locked. But that needs another thread.
#else
- LOG(INFO) << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA;
+ LOG(INFO) << "Skipping unlock_object as I don't know how to do that on " << kRuntimeISA;
// Force-print to std::cout so it's also outside the logcat.
- std::cout << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA << std::endl;
+ std::cout << "Skipping unlock_object as I don't know how to do that on " << kRuntimeISA << std::endl;
#endif
}
@@ -699,6 +846,8 @@
}
}
+ // TODO: Deallocate things.
+
// Tests done.
#else
LOG(INFO) << "Skipping string_compareto as I don't know how to do that on " << kRuntimeISA;