Skip to content

Commit

Permalink
Revert "Use try lock to fix class resolution race"
Browse files Browse the repository at this point in the history
This reverts commit a704eda0078989a73cac111ed309aca50d2e289b.

Bug: 27417671
Bug: 30500547

(cherry picked from commit 69bf969c055c31a75d17ea92aeee756042678114)

Change-Id: I4354d1c9f1c554f054e99efd7aa52d8a2c5d402c
  • Loading branch information
Mathieu Chartier committed Aug 24, 2016
1 parent 1386f86 commit adc538a
Show file tree
Hide file tree
Showing 8 changed files with 29 additions and 171 deletions.
33 changes: 10 additions & 23 deletions runtime/class_linker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2179,33 +2179,20 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
}

// Wait for the class if it has not already been linked.
size_t index = 0;
// Maximum number of yield iterations until we start sleeping.
static const size_t kNumYieldIterations = 1000;
// How long each sleep is in us.
static const size_t kSleepDurationUS = 1000; // 1 ms.
while (!klass->IsResolved() && !klass->IsErroneous()) {
if (!klass->IsResolved() && !klass->IsErroneous()) {
StackHandleScope<1> hs(self);
HandleWrapper<mirror::Class> h_class(hs.NewHandleWrapper(&klass));
{
ObjectTryLock<mirror::Class> lock(self, h_class);
// Can not use a monitor wait here since it may block when returning and deadlock if another
// thread has locked klass.
if (lock.Acquired()) {
// Check for circular dependencies between classes, the lock is required for SetStatus.
if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) {
ThrowClassCircularityError(h_class.Get());
mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self);
return nullptr;
}
}
ObjectLock<mirror::Class> lock(self, h_class);
// Check for circular dependencies between classes.
if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) {
ThrowClassCircularityError(h_class.Get());
mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self);
return nullptr;
}
if (index < kNumYieldIterations) {
sched_yield();
} else {
usleep(kSleepDurationUS);
// Wait for the pending initialization to complete.
while (!h_class->IsResolved() && !h_class->IsErroneous()) {
lock.WaitIgnoringInterrupts();
}
++index;
}

if (klass->IsErroneous()) {
Expand Down
6 changes: 1 addition & 5 deletions runtime/mirror/object-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,7 @@ inline uint32_t Object::GetLockOwnerThreadId() {
}

inline mirror::Object* Object::MonitorEnter(Thread* self) {
return Monitor::MonitorEnter(self, this, /*trylock*/false);
}

inline mirror::Object* Object::MonitorTryEnter(Thread* self) {
return Monitor::MonitorEnter(self, this, /*trylock*/true);
return Monitor::MonitorEnter(self, this);
}

inline bool Object::MonitorExit(Thread* self) {
Expand Down
5 changes: 0 additions & 5 deletions runtime/mirror/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,6 @@ class MANAGED LOCKABLE Object {
SHARED_REQUIRES(Locks::mutator_lock_);
uint32_t GetLockOwnerThreadId();

// Try to enter the monitor, returns non null if we succeeded.
mirror::Object* MonitorTryEnter(Thread* self)
EXCLUSIVE_LOCK_FUNCTION()
REQUIRES(!Roles::uninterruptible_)
SHARED_REQUIRES(Locks::mutator_lock_);
mirror::Object* MonitorEnter(Thread* self)
EXCLUSIVE_LOCK_FUNCTION()
REQUIRES(!Roles::uninterruptible_)
Expand Down
52 changes: 17 additions & 35 deletions runtime/monitor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -314,34 +314,21 @@ std::string Monitor::PrettyContentionInfo(const std::string& owner_name,
return oss.str();
}

bool Monitor::TryLockLocked(Thread* self) {
if (owner_ == nullptr) { // Unowned.
owner_ = self;
CHECK_EQ(lock_count_, 0);
// When debugging, save the current monitor holder for future
// acquisition failures to use in sampled logging.
if (lock_profiling_threshold_ != 0) {
locking_method_ = self->GetCurrentMethod(&locking_dex_pc_);
}
} else if (owner_ == self) { // Recursive.
lock_count_++;
} else {
return false;
}
AtraceMonitorLock(self, GetObject(), false /* is_wait */);
return true;
}

bool Monitor::TryLock(Thread* self) {
MutexLock mu(self, monitor_lock_);
return TryLockLocked(self);
}

void Monitor::Lock(Thread* self) {
MutexLock mu(self, monitor_lock_);
while (true) {
if (TryLockLocked(self)) {
return;
if (owner_ == nullptr) { // Unowned.
owner_ = self;
CHECK_EQ(lock_count_, 0);
// When debugging, save the current monitor holder for future
// acquisition failures to use in sampled logging.
if (lock_profiling_threshold_ != 0) {
locking_method_ = self->GetCurrentMethod(&locking_dex_pc_);
}
break;
} else if (owner_ == self) { // Recursive.
lock_count_++;
break;
}
// Contended.
const bool log_contention = (lock_profiling_threshold_ != 0);
Expand Down Expand Up @@ -443,6 +430,8 @@ void Monitor::Lock(Thread* self) {
monitor_lock_.Lock(self); // Reacquire locks in order.
--num_waiters_;
}

AtraceMonitorLock(self, GetObject(), false /* is_wait */);
}

static void ThrowIllegalMonitorStateExceptionF(const char* fmt, ...)
Expand Down Expand Up @@ -863,7 +852,7 @@ static mirror::Object* FakeUnlock(mirror::Object* obj)
return obj;
}

mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj, bool trylock) {
mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj) {
DCHECK(self != nullptr);
DCHECK(obj != nullptr);
self->AssertThreadSuspensionIsAllowable();
Expand Down Expand Up @@ -909,9 +898,6 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj, bool tr
InflateThinLocked(self, h_obj, lock_word, 0);
}
} else {
if (trylock) {
return nullptr;
}
// Contention.
contention_count++;
Runtime* runtime = Runtime::Current();
Expand All @@ -930,12 +916,8 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj, bool tr
}
case LockWord::kFatLocked: {
Monitor* mon = lock_word.FatLockMonitor();
if (trylock) {
return mon->TryLock(self) ? h_obj.Get() : nullptr;
} else {
mon->Lock(self);
return h_obj.Get(); // Success!
}
mon->Lock(self);
return h_obj.Get(); // Success!
}
case LockWord::kHashCode:
// Inflate with the existing hashcode.
Expand Down
11 changes: 1 addition & 10 deletions runtime/monitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class Monitor {
NO_THREAD_SAFETY_ANALYSIS; // TODO: Reading lock owner without holding lock is racy.

// NO_THREAD_SAFETY_ANALYSIS for mon->Lock.
static mirror::Object* MonitorEnter(Thread* thread, mirror::Object* obj, bool trylock)
static mirror::Object* MonitorEnter(Thread* thread, mirror::Object* obj)
EXCLUSIVE_LOCK_FUNCTION(obj)
NO_THREAD_SAFETY_ANALYSIS
REQUIRES(!Roles::uninterruptible_)
Expand Down Expand Up @@ -193,15 +193,6 @@ class Monitor {
!monitor_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);

// Try to lock without blocking, returns true if we acquired the lock.
bool TryLock(Thread* self)
REQUIRES(!monitor_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
// Variant for already holding the monitor lock.
bool TryLockLocked(Thread* self)
REQUIRES(monitor_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);

void Lock(Thread* self)
REQUIRES(!monitor_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
Expand Down
57 changes: 0 additions & 57 deletions runtime/monitor_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#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"

Expand Down Expand Up @@ -375,60 +374,4 @@ 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
15 changes: 0 additions & 15 deletions runtime/object_lock.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,7 @@ void ObjectLock<T>::NotifyAll() {
obj_->NotifyAll(self_);
}

template <typename T>
ObjectTryLock<T>::ObjectTryLock(Thread* self, Handle<T> object) : self_(self), obj_(object) {
CHECK(object.Get() != nullptr);
acquired_ = obj_->MonitorTryEnter(self_) != nullptr;
}

template <typename T>
ObjectTryLock<T>::~ObjectTryLock() {
if (acquired_) {
obj_->MonitorExit(self_);
}
}

template class ObjectLock<mirror::Class>;
template class ObjectLock<mirror::Object>;
template class ObjectTryLock<mirror::Class>;
template class ObjectTryLock<mirror::Object>;

} // namespace art
21 changes: 0 additions & 21 deletions runtime/object_lock.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,27 +45,6 @@ class ObjectLock {
DISALLOW_COPY_AND_ASSIGN(ObjectLock);
};

template <typename T>
class ObjectTryLock {
public:
ObjectTryLock(Thread* self, Handle<T> object) SHARED_REQUIRES(Locks::mutator_lock_);

~ObjectTryLock() SHARED_REQUIRES(Locks::mutator_lock_);

bool Acquired() const {
return acquired_;
}

private:
Thread* const self_;
Handle<T> const obj_;
bool acquired_;


DISALLOW_COPY_AND_ASSIGN(ObjectTryLock);
};


} // namespace art

#endif // ART_RUNTIME_OBJECT_LOCK_H_

0 comments on commit adc538a

Please sign in to comment.