diff options
author | 2016-07-13 09:53:35 -0700 | |
---|---|---|
committer | 2016-07-27 14:37:42 -0700 | |
commit | a704eda0078989a73cac111ed309aca50d2e289b (patch) | |
tree | 07fd076b3ea6f86bfea71e4c6707fd301f108db5 /runtime/monitor_test.cc | |
parent | 9755c262df1be7f5d5b98d038c8fd3734e974f9d (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.cc | 57 |
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 |