summaryrefslogtreecommitdiff
path: root/runtime/monitor_test.cc
diff options
context:
space:
mode:
author Mathieu Chartier <mathieuc@google.com> 2016-07-13 09:53:35 -0700
committer Mathieu Chartier <mathieuc@google.com> 2016-07-27 14:37:42 -0700
commita704eda0078989a73cac111ed309aca50d2e289b (patch)
tree07fd076b3ea6f86bfea71e4c6707fd301f108db5 /runtime/monitor_test.cc
parent9755c262df1be7f5d5b98d038c8fd3734e974f9d (diff)
Use try lock to fix class resolution race
There was some possible deadlocks related to EnsureResolved caused by acquiring an object lock. Scenario: Thread 1 acquires lock on obj1 Thread 1 begins to resolve / initialize class1 Thread 1 blocks since it sees that class1 is already being resolved and gets preempted before it can acquire the object lock on class1 Thread 2 finishes resolving and initializing class1 and locks class1 Thread 2 blocks attempting to lock obj1 Thread 1 blocks attempting to lock class1 Deadlock Fixed the deadlock by changing EnsureResolved to use a try lock for the unresolved case. Added a test. Test: Device boot, test-art-host, monitor_test Bug: 27417671 Change-Id: Ic6e1c3ca6f45490cf8a7bf8e137dee71ac83ff64
Diffstat (limited to 'runtime/monitor_test.cc')
-rw-r--r--runtime/monitor_test.cc57
1 files changed, 57 insertions, 0 deletions
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index 83e0c0dea9..48d256c985 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -26,6 +26,7 @@
#include "handle_scope-inl.h"
#include "mirror/class-inl.h"
#include "mirror/string-inl.h" // Strings are easiest to allocate
+#include "object_lock.h"
#include "scoped_thread_state_change.h"
#include "thread_pool.h"
@@ -374,4 +375,60 @@ TEST_F(MonitorTest, CheckExceptionsWait3) {
"Monitor test thread pool 3");
}
+class TryLockTask : public Task {
+ public:
+ explicit TryLockTask(Handle<mirror::Object> obj) : obj_(obj) {}
+
+ void Run(Thread* self) {
+ ScopedObjectAccess soa(self);
+ // Lock is held by other thread, try lock should fail.
+ ObjectTryLock<mirror::Object> lock(self, obj_);
+ EXPECT_FALSE(lock.Acquired());
+ }
+
+ void Finalize() {
+ delete this;
+ }
+
+ private:
+ Handle<mirror::Object> obj_;
+};
+
+// Test trylock in deadlock scenarios.
+TEST_F(MonitorTest, TestTryLock) {
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
+ Thread* const self = Thread::Current();
+ ThreadPool thread_pool("the pool", 2);
+ ScopedObjectAccess soa(self);
+ StackHandleScope<3> hs(self);
+ Handle<mirror::Object> obj1(
+ hs.NewHandle<mirror::Object>(mirror::String::AllocFromModifiedUtf8(self, "hello, world!")));
+ Handle<mirror::Object> obj2(
+ hs.NewHandle<mirror::Object>(mirror::String::AllocFromModifiedUtf8(self, "hello, world!")));
+ {
+ ObjectLock<mirror::Object> lock1(self, obj1);
+ ObjectLock<mirror::Object> lock2(self, obj1);
+ {
+ ObjectTryLock<mirror::Object> trylock(self, obj1);
+ EXPECT_TRUE(trylock.Acquired());
+ }
+ // Test failure case.
+ thread_pool.AddTask(self, new TryLockTask(obj1));
+ thread_pool.StartWorkers(self);
+ ScopedThreadSuspension sts(self, kSuspended);
+ thread_pool.Wait(Thread::Current(), /*do_work*/false, /*may_hold_locks*/false);
+ }
+ // Test that the trylock actually locks the object.
+ {
+ ObjectTryLock<mirror::Object> trylock(self, obj1);
+ EXPECT_TRUE(trylock.Acquired());
+ obj1->Notify(self);
+ // Since we hold the lock there should be no monitor state exeception.
+ self->AssertNoPendingException();
+ }
+ thread_pool.StopWorkers(self);
+}
+
+
} // namespace art