Remove lock from ClassExt installation procedure.
We were using a lock on the class to ensure that we avoid races in
setting the ext_data_ field of a class object. We replace this with a
CAS of the field in order to prevent deadlocks.
Test: mma test-art-host
Change-Id: Ie436ff9526f2c3b38a9af49c5606a7cee6d718f1
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 03d6487..db46027 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -64,19 +64,45 @@
return GetFieldObject<ClassExt>(OFFSET_OF_OBJECT_MEMBER(Class, ext_data_));
}
-void Class::SetExtData(ObjPtr<ClassExt> ext) {
- CHECK(ext != nullptr) << PrettyClass();
- // TODO It might be wise to just create an internal (global?) mutex that we synchronize on instead
- // to prevent any possibility of deadlocks with java code. Alternatively we might want to come up
- // with some other abstraction.
- DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId())
- << "The " << PrettyClass() << " object should be locked when writing to the extData field.";
- DCHECK(GetExtData() == nullptr)
- << "The extData for " << PrettyClass() << " has already been set!";
- if (Runtime::Current()->IsActiveTransaction()) {
- SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, ext_data_), ext);
+ClassExt* Class::EnsureExtDataPresent(Thread* self) {
+ ObjPtr<ClassExt> existing(GetExtData());
+ if (!existing.IsNull()) {
+ return existing.Ptr();
+ }
+ StackHandleScope<3> hs(self);
+ // Handlerize 'this' since we are allocating here.
+ Handle<Class> h_this(hs.NewHandle(this));
+ // Clear exception so we can allocate.
+ Handle<Throwable> throwable(hs.NewHandle(self->GetException()));
+ self->ClearException();
+ // Allocate the ClassExt
+ Handle<ClassExt> new_ext(hs.NewHandle(ClassExt::Alloc(self)));
+ if (new_ext.Get() == nullptr) {
+ // OOM allocating the classExt.
+ // TODO Should we restore the suppressed exception?
+ self->AssertPendingOOMException();
+ return nullptr;
} else {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, ext_data_), ext);
+ MemberOffset ext_offset(OFFSET_OF_OBJECT_MEMBER(Class, ext_data_));
+ bool set;
+ // Set the ext_data_ field using CAS semantics.
+ if (Runtime::Current()->IsActiveTransaction()) {
+ set = h_this->CasFieldStrongSequentiallyConsistentObject<true>(ext_offset,
+ ObjPtr<ClassExt>(nullptr),
+ new_ext.Get());
+ } else {
+ set = h_this->CasFieldStrongSequentiallyConsistentObject<false>(ext_offset,
+ ObjPtr<ClassExt>(nullptr),
+ new_ext.Get());
+ }
+ ObjPtr<ClassExt> ret(set ? new_ext.Get() : h_this->GetExtData());
+ DCHECK(!set || h_this->GetExtData() == new_ext.Get());
+ CHECK(!ret.IsNull());
+ // Restore the exception if there was one.
+ if (throwable.Get() != nullptr) {
+ self->SetException(throwable.Get());
+ }
+ return ret.Ptr();
}
}
@@ -108,34 +134,16 @@
}
}
- {
- // Ensure we lock around 'this' when we set the ClassExt.
- ObjectLock<mirror::Class> lock(self, h_this);
- StackHandleScope<2> hs(self);
- // Remember the current exception.
- Handle<Throwable> exception(hs.NewHandle(self->GetException()));
- CHECK(exception.Get() != nullptr);
- MutableHandle<ClassExt> ext(hs.NewHandle(h_this->GetExtData()));
- if (ext.Get() == nullptr) {
- // Cannot have exception while allocating.
- self->ClearException();
- ext.Assign(ClassExt::Alloc(self));
- DCHECK(ext.Get() == nullptr || ext->GetVerifyError() == nullptr);
- if (ext.Get() != nullptr) {
- self->AssertNoPendingException();
- h_this->SetExtData(ext.Get());
- self->SetException(exception.Get());
- } else {
- // TODO Should we restore the old exception anyway?
- self->AssertPendingOOMException();
- }
- }
- if (ext.Get() != nullptr) {
- ext->SetVerifyError(self->GetException());
- }
+ ObjPtr<ClassExt> ext(h_this->EnsureExtDataPresent(self));
+ if (!ext.IsNull()) {
+ self->AssertPendingException();
+ ext->SetVerifyError(self->GetException());
+ } else {
+ self->AssertPendingOOMException();
}
self->AssertPendingException();
}
+
static_assert(sizeof(Status) == sizeof(uint32_t), "Size of status not equal to uint32");
if (Runtime::Current()->IsActiveTransaction()) {
h_this->SetField32Volatile<true>(StatusOffset(), new_status);