summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
authorSebastien Hertz <shertz@google.com>2014-01-15 10:20:56 +0100
committerSebastien Hertz <shertz@google.com>2014-02-17 11:32:15 +0100
commitd2fe10a3a34af171bf1631219cd2d6ff6b7778b5 (patch)
treeb6b7eb8eba23a5c2723518da99c03bf47b97f58a /runtime
parent5a3f55ad9519e87c0d3bbddaf3d8a186a887a79b (diff)
downloadart-d2fe10a3a34af171bf1631219cd2d6ff6b7778b5.zip
art-d2fe10a3a34af171bf1631219cd2d6ff6b7778b5.tar.gz
art-d2fe10a3a34af171bf1631219cd2d6ff6b7778b5.tar.bz2
Remove blacklist
Removes the class initialization blacklist and use transaction to detect and revert class initialization attempting to invoke native method. This only concerns class initialization happening at compilation time when generating an image (like boot.art for the system). In transactional mode, we log every object's field assignment and array update. Therefore we're able to abort a transaction to restore values of fields and array as they were before the transaction starts. We also log changes to the intern string table so we can restore its state prior to transaction start. Since transactional mode only happens at compilation time, we don't need to log all these changes at runtime. In order to reduce the overhead of testing if transactional mode is on/off, we templatize interfaces of mirror::Object and mirror::Array, respectively responsible for setting a field and setting an array element. For various reasons, we skip some specific fields from transaction: - Object's class and array's length must remain unchanged so garbage collector can compute object's size. - Immutable fields only set during class loading: list of fields, method, dex caches, vtables, ... as all classes have been loaded and verified before a transaction occurs. - Object's monitor for performance reason. Before generating the image, we browse the heap to collect objects that need to be written into it. Since the heap may still holds references to unreachable objects due to aborted transactions, we trigger one collection at the end of the class preinitialization phase. Since the transaction is held by the runtime and all compilation threads share the same runtime, we need to ensure only one compilation thread has exclusive access to the runtime. To workaround this issue, we force class initialization phase to run with only one thread. Note this is only done when generating image so application compilation is not impacted. This issue will be addressed in a separate CL. Bug: 9676614 Change-Id: I221910a9183a5ba6c2b99a277f5a5a68bc69b5f9
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Android.mk1
-rw-r--r--runtime/class_linker.cc36
-rw-r--r--runtime/class_linker_test.cc19
-rw-r--r--runtime/debugger.cc11
-rw-r--r--runtime/dex_file.cc25
-rw-r--r--runtime/dex_file.h1
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc2
-rw-r--r--runtime/entrypoints/entrypoint_utils.h1
-rw-r--r--runtime/entrypoints/portable/portable_field_entrypoints.cc36
-rw-r--r--runtime/entrypoints/quick/quick_field_entrypoints.cc36
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc4
-rw-r--r--runtime/gc/accounting/mod_union_table.cc6
-rw-r--r--runtime/gc/collector/semi_space.cc4
-rw-r--r--runtime/gc/heap.cc2
-rw-r--r--runtime/gc/heap_test.cc2
-rw-r--r--runtime/gc/reference_queue.cc33
-rw-r--r--runtime/intern_table.cc83
-rw-r--r--runtime/intern_table.h39
-rw-r--r--runtime/interpreter/interpreter.cc43
-rw-r--r--runtime/interpreter/interpreter_common.cc73
-rw-r--r--runtime/interpreter/interpreter_common.h69
-rw-r--r--runtime/interpreter/interpreter_goto_table_impl.cc83
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc83
-rw-r--r--runtime/jni_internal.cc12
-rw-r--r--runtime/locks.cc4
-rw-r--r--runtime/locks.h5
-rw-r--r--runtime/mirror/array.cc3
-rw-r--r--runtime/mirror/array.h29
-rw-r--r--runtime/mirror/art_field-inl.h71
-rw-r--r--runtime/mirror/art_field.cc15
-rw-r--r--runtime/mirror/art_field.h18
-rw-r--r--runtime/mirror/art_method-inl.h3
-rw-r--r--runtime/mirror/art_method.cc20
-rw-r--r--runtime/mirror/art_method.h49
-rw-r--r--runtime/mirror/class-inl.h40
-rw-r--r--runtime/mirror/class.cc27
-rw-r--r--runtime/mirror/class.h37
-rw-r--r--runtime/mirror/dex_cache.cc16
-rw-r--r--runtime/mirror/dex_cache.h4
-rw-r--r--runtime/mirror/iftable-inl.h2
-rw-r--r--runtime/mirror/iftable.h2
-rw-r--r--runtime/mirror/object-inl.h76
-rw-r--r--runtime/mirror/object.h21
-rw-r--r--runtime/mirror/object_array-inl.h25
-rw-r--r--runtime/mirror/object_array.h16
-rw-r--r--runtime/mirror/object_test.cc20
-rw-r--r--runtime/mirror/stack_trace_element.cc26
-rw-r--r--runtime/mirror/stack_trace_element.h5
-rw-r--r--runtime/mirror/string.cc16
-rw-r--r--runtime/mirror/string.h11
-rw-r--r--runtime/mirror/throwable.cc6
-rw-r--r--runtime/mirror/throwable.h8
-rw-r--r--runtime/native/java_lang_reflect_Field.cc18
-rw-r--r--runtime/native/sun_misc_Unsafe.cc36
-rw-r--r--runtime/runtime.cc83
-rw-r--r--runtime/runtime.h26
-rw-r--r--runtime/thread.cc48
-rw-r--r--runtime/thread.h5
-rw-r--r--runtime/transaction.cc419
-rw-r--r--runtime/transaction.h195
-rw-r--r--runtime/transaction_test.cc472
61 files changed, 2149 insertions, 432 deletions
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 223ae7c..b6b4ff9 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -136,6 +136,7 @@ LIBART_COMMON_SRC_FILES := \
thread_pool.cc \
throw_location.cc \
trace.cc \
+ transaction.cc \
profiler.cc \
utf.cc \
utils.cc \
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index fac1e14..3863ee5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2832,8 +2832,8 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccess& soa, jstring na
return nullptr;
}
- interfaces_sfield->SetObject(klass.get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
- throws_sfield->SetObject(klass.get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws));
+ interfaces_sfield->SetObject<false>(klass.get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+ throws_sfield->SetObject<false>(klass.get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws));
klass->SetStatus(mirror::Class::kStatusInitialized, self);
}
@@ -3144,7 +3144,11 @@ bool ClassLinker::InitializeClass(const SirtRef<mirror::Class>& klass, bool can_
SafeMap<uint32_t, mirror::ArtField*> field_map;
ConstructFieldMap(dex_file, *dex_class_def, klass.get(), field_map);
for (size_t i = 0; it.HasNext(); i++, it.Next()) {
- it.ReadValueToField(field_map.Get(i));
+ if (Runtime::Current()->IsActiveTransaction()) {
+ it.ReadValueToField<true>(field_map.Get(i));
+ } else {
+ it.ReadValueToField<false>(field_map.Get(i));
+ }
}
}
}
@@ -3530,7 +3534,7 @@ bool ClassLinker::LinkVirtualMethods(const SirtRef<mirror::Class>& klass) {
super_mh.GetDeclaringClassDescriptor());
return false;
}
- vtable->Set(j, local_method);
+ vtable->Set<false>(j, local_method);
local_method->SetMethodIndex(j);
break;
} else {
@@ -3542,7 +3546,7 @@ bool ClassLinker::LinkVirtualMethods(const SirtRef<mirror::Class>& klass) {
}
if (j == actual_count) {
// Not overriding, append.
- vtable->Set(actual_count, local_method);
+ vtable->Set<false>(actual_count, local_method);
local_method->SetMethodIndex(actual_count);
actual_count += 1;
}
@@ -3576,7 +3580,7 @@ bool ClassLinker::LinkVirtualMethods(const SirtRef<mirror::Class>& klass) {
}
for (size_t i = 0; i < num_virtual_methods; ++i) {
mirror::ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i);
- vtable->Set(i, virtual_method);
+ vtable->Set<false>(i, virtual_method);
virtual_method->SetMethodIndex(i & 0xFFFF);
}
klass->SetVTable(vtable.get());
@@ -3745,14 +3749,14 @@ bool ClassLinker::LinkInterfaceMethods(const SirtRef<mirror::Class>& klass,
PrettyMethod(interface_method).c_str());
return false;
}
- method_array->Set(j, vtable_method);
+ method_array->Set<false>(j, vtable_method);
// Place method in imt if entry is empty, place conflict otherwise.
uint32_t imt_index = interface_method->GetDexMethodIndex() % kImtSize;
if (imtable->Get(imt_index) == NULL) {
- imtable->Set(imt_index, vtable_method);
+ imtable->Set<false>(imt_index, vtable_method);
imtable_changed = true;
} else {
- imtable->Set(imt_index, Runtime::Current()->GetImtConflictMethod());
+ imtable->Set<false>(imt_index, Runtime::Current()->GetImtConflictMethod());
}
break;
}
@@ -3777,7 +3781,7 @@ bool ClassLinker::LinkInterfaceMethods(const SirtRef<mirror::Class>& klass,
// TODO: If a methods move then the miranda_list may hold stale references.
miranda_list.push_back(miranda_method.get());
}
- method_array->Set(j, miranda_method.get());
+ method_array->Set<false>(j, miranda_method.get());
}
}
}
@@ -3787,7 +3791,7 @@ bool ClassLinker::LinkInterfaceMethods(const SirtRef<mirror::Class>& klass,
mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod();
for (size_t i = 0; i < kImtSize; i++) {
if (imtable->Get(i) == NULL) {
- imtable->Set(i, imt_conflict_method);
+ imtable->Set<false>(i, imt_conflict_method);
}
}
klass->SetImTable(imtable.get());
@@ -3823,7 +3827,7 @@ bool ClassLinker::LinkInterfaceMethods(const SirtRef<mirror::Class>& klass,
method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda);
method->SetMethodIndex(0xFFFF & (old_vtable_count + i));
klass->SetVirtualMethod(old_method_count + i, method);
- vtable->Set(old_vtable_count + i, method);
+ vtable->Set<false>(old_vtable_count + i, method);
}
// TODO: do not assign to the vtable field until it is fully constructed.
klass->SetVTable(vtable.get());
@@ -3929,7 +3933,7 @@ bool ClassLinker::LinkFields(const SirtRef<mirror::Class>& klass, bool is_static
}
grouped_and_sorted_fields.pop_front();
num_reference_fields++;
- fields->Set(current_field, field);
+ fields->Set<false>(current_field, field);
field->SetOffset(field_offset);
field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
}
@@ -3946,7 +3950,7 @@ bool ClassLinker::LinkFields(const SirtRef<mirror::Class>& klass, bool is_static
if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) {
continue;
}
- fields->Set(current_field++, field);
+ fields->Set<false>(current_field++, field);
field->SetOffset(field_offset);
// drop the consumed field
grouped_and_sorted_fields.erase(grouped_and_sorted_fields.begin() + i);
@@ -3965,7 +3969,7 @@ bool ClassLinker::LinkFields(const SirtRef<mirror::Class>& klass, bool is_static
FieldHelper fh(field);
Primitive::Type type = fh.GetTypeAsPrimitiveType();
CHECK(type != Primitive::kPrimNot); // should only be working on primitive types
- fields->Set(current_field, field);
+ fields->Set<false>(current_field, field);
field->SetOffset(field_offset);
field_offset = MemberOffset(field_offset.Uint32Value() +
((type == Primitive::kPrimLong || type == Primitive::kPrimDouble)
@@ -4418,7 +4422,7 @@ void ClassLinker::SetClassRoot(ClassRoot class_root, mirror::Class* klass) {
DCHECK(class_roots_ != NULL);
DCHECK(class_roots_->Get(class_root) == NULL);
- class_roots_->Set(class_root, klass);
+ class_roots_->Set<false>(class_root, klass);
}
} // namespace art
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index d9ef0c1..28ed6c4 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -867,55 +867,56 @@ TEST_F(ClassLinkerTest, StaticFields) {
EXPECT_STREQ(ClassHelper(s0->GetClass()).GetDescriptor(), "Ljava/lang/reflect/ArtField;");
EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimBoolean);
EXPECT_EQ(true, s0->GetBoolean(statics.get()));
- s0->SetBoolean(statics.get(), false);
+ s0->SetBoolean<false>(statics.get(), false);
mirror::ArtField* s1 = statics->FindStaticField("s1", "B");
fh.ChangeField(s1);
EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimByte);
EXPECT_EQ(5, s1->GetByte(statics.get()));
- s1->SetByte(statics.get(), 6);
+ s1->SetByte<false>(statics.get(), 6);
mirror::ArtField* s2 = statics->FindStaticField("s2", "C");
fh.ChangeField(s2);
EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimChar);
EXPECT_EQ('a', s2->GetChar(statics.get()));
- s2->SetChar(statics.get(), 'b');
+ s2->SetChar<false>(statics.get(), 'b');
mirror::ArtField* s3 = statics->FindStaticField("s3", "S");
fh.ChangeField(s3);
EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimShort);
EXPECT_EQ(-536, s3->GetShort(statics.get()));
- s3->SetShort(statics.get(), -535);
+ s3->SetShort<false>(statics.get(), -535);
mirror::ArtField* s4 = statics->FindStaticField("s4", "I");
fh.ChangeField(s4);
EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimInt);
EXPECT_EQ(2000000000, s4->GetInt(statics.get()));
- s4->SetInt(statics.get(), 2000000001);
+ s4->SetInt<false>(statics.get(), 2000000001);
mirror::ArtField* s5 = statics->FindStaticField("s5", "J");
fh.ChangeField(s5);
EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimLong);
EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong(statics.get()));
- s5->SetLong(statics.get(), 0x34567890abcdef12LL);
+ s5->SetLong<false>(statics.get(), 0x34567890abcdef12LL);
mirror::ArtField* s6 = statics->FindStaticField("s6", "F");
fh.ChangeField(s6);
EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimFloat);
EXPECT_EQ(0.5, s6->GetFloat(statics.get()));
- s6->SetFloat(statics.get(), 0.75);
+ s6->SetFloat<false>(statics.get(), 0.75);
mirror::ArtField* s7 = statics->FindStaticField("s7", "D");
fh.ChangeField(s7);
EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimDouble);
EXPECT_EQ(16777217, s7->GetDouble(statics.get()));
- s7->SetDouble(statics.get(), 16777219);
+ s7->SetDouble<false>(statics.get(), 16777219);
mirror::ArtField* s8 = statics->FindStaticField("s8", "Ljava/lang/String;");
fh.ChangeField(s8);
EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimNot);
EXPECT_TRUE(s8->GetObject(statics.get())->AsString()->Equals("android"));
- s8->SetObject(s8->GetDeclaringClass(), mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot"));
+ s8->SetObject<false>(s8->GetDeclaringClass(),
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot"));
// TODO: Remove EXPECT_FALSE when GCC can handle EXPECT_EQ
// http://code.google.com/p/googletest/issues/detail?id=322
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 733e843..20ad372 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -1149,7 +1149,7 @@ JDWP::JdwpError Dbg::SetArrayElements(JDWP::ObjectId array_id, int offset, int c
if (o == ObjectRegistry::kInvalidObject) {
return JDWP::ERR_INVALID_OBJECT;
}
- oa->Set(offset + i, o);
+ oa->Set<false>(offset + i, o);
}
}
@@ -1582,10 +1582,12 @@ static JDWP::JdwpError SetFieldValueImpl(JDWP::ObjectId object_id, JDWP::FieldId
if (IsPrimitiveTag(tag)) {
if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
CHECK_EQ(width, 8);
- f->Set64(o, value);
+ // Debugging can't use transactional mode (runtime only).
+ f->Set64<false>(o, value);
} else {
CHECK_LE(width, 4);
- f->Set32(o, value);
+ // Debugging can't use transactional mode (runtime only).
+ f->Set32<false>(o, value);
}
} else {
mirror::Object* v = gRegistry->Get<mirror::Object*>(value);
@@ -1598,7 +1600,8 @@ static JDWP::JdwpError SetFieldValueImpl(JDWP::ObjectId object_id, JDWP::FieldId
return JDWP::ERR_INVALID_OBJECT;
}
}
- f->SetObject(o, v);
+ // Debugging can't use transactional mode (runtime only).
+ f->SetObject<false>(o, v);
}
return JDWP::ERR_NONE;
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index eaba7eb..5e2b9ff 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -1082,33 +1082,36 @@ void EncodedStaticFieldValueIterator::Next() {
ptr_ += width;
}
+template<bool kTransactionActive>
void EncodedStaticFieldValueIterator::ReadValueToField(mirror::ArtField* field) const {
switch (type_) {
- case kBoolean: field->SetBoolean(field->GetDeclaringClass(), jval_.z); break;
- case kByte: field->SetByte(field->GetDeclaringClass(), jval_.b); break;
- case kShort: field->SetShort(field->GetDeclaringClass(), jval_.s); break;
- case kChar: field->SetChar(field->GetDeclaringClass(), jval_.c); break;
- case kInt: field->SetInt(field->GetDeclaringClass(), jval_.i); break;
- case kLong: field->SetLong(field->GetDeclaringClass(), jval_.j); break;
- case kFloat: field->SetFloat(field->GetDeclaringClass(), jval_.f); break;
- case kDouble: field->SetDouble(field->GetDeclaringClass(), jval_.d); break;
- case kNull: field->SetObject(field->GetDeclaringClass(), NULL); break;
+ case kBoolean: field->SetBoolean<kTransactionActive>(field->GetDeclaringClass(), jval_.z); break;
+ case kByte: field->SetByte<kTransactionActive>(field->GetDeclaringClass(), jval_.b); break;
+ case kShort: field->SetShort<kTransactionActive>(field->GetDeclaringClass(), jval_.s); break;
+ case kChar: field->SetChar<kTransactionActive>(field->GetDeclaringClass(), jval_.c); break;
+ case kInt: field->SetInt<kTransactionActive>(field->GetDeclaringClass(), jval_.i); break;
+ case kLong: field->SetLong<kTransactionActive>(field->GetDeclaringClass(), jval_.j); break;
+ case kFloat: field->SetFloat<kTransactionActive>(field->GetDeclaringClass(), jval_.f); break;
+ case kDouble: field->SetDouble<kTransactionActive>(field->GetDeclaringClass(), jval_.d); break;
+ case kNull: field->SetObject<kTransactionActive>(field->GetDeclaringClass(), NULL); break;
case kString: {
CHECK(!kMovingFields);
mirror::String* resolved = linker_->ResolveString(dex_file_, jval_.i, *dex_cache_);
- field->SetObject(field->GetDeclaringClass(), resolved);
+ field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved);
break;
}
case kType: {
CHECK(!kMovingFields);
mirror::Class* resolved = linker_->ResolveType(dex_file_, jval_.i, *dex_cache_,
*class_loader_);
- field->SetObject(field->GetDeclaringClass(), resolved);
+ field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved);
break;
}
default: UNIMPLEMENTED(FATAL) << ": type " << type_;
}
}
+template void EncodedStaticFieldValueIterator::ReadValueToField<true>(mirror::ArtField* field) const;
+template void EncodedStaticFieldValueIterator::ReadValueToField<false>(mirror::ArtField* field) const;
CatchHandlerIterator::CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address) {
handler_.address_ = -1;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 46df455..e9d18b5 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1142,6 +1142,7 @@ class EncodedStaticFieldValueIterator {
ClassLinker* linker, const DexFile::ClassDef& class_def)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void ReadValueToField(mirror::ArtField* field) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool HasNext() { return pos_ < array_size_; }
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 4e58a72..4078cac 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -162,7 +162,7 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessUnchecked& soa, const char
CHECK(soa.Self()->IsExceptionPending());
return zero;
}
- soa.Decode<mirror::ObjectArray<mirror::Object>* >(args_jobj)->Set(i, val);
+ soa.Decode<mirror::ObjectArray<mirror::Object>* >(args_jobj)->Set<false>(i, val);
}
}
}
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index c3deba5..8b94b5a 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -136,6 +136,7 @@ ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCode(uint32_t type_id
gc::Heap* heap = Runtime::Current()->GetHeap();
return klass->Alloc<kInstrumented>(self, heap->GetCurrentAllocator());
}
+ DCHECK(klass != nullptr);
return klass->Alloc<kInstrumented>(self, allocator_type);
}
diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc
index 0b54b9c..f48f1a9 100644
--- a/runtime/entrypoints/portable/portable_field_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_field_entrypoints.cc
@@ -30,13 +30,15 @@ extern "C" int32_t art_portable_set32_static_from_code(uint32_t field_idx,
StaticPrimitiveWrite,
sizeof(uint32_t));
if (LIKELY(field != NULL)) {
- field->Set32(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->Set32<false>(field->GetDeclaringClass(), new_value);
return 0;
}
field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
sizeof(uint32_t));
if (LIKELY(field != NULL)) {
- field->Set32(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->Set32<false>(field->GetDeclaringClass(), new_value);
return 0;
}
return -1;
@@ -48,13 +50,15 @@ extern "C" int32_t art_portable_set64_static_from_code(uint32_t field_idx,
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(uint64_t));
if (LIKELY(field != NULL)) {
- field->Set64(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->Set64<false>(field->GetDeclaringClass(), new_value);
return 0;
}
field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
sizeof(uint64_t));
if (LIKELY(field != NULL)) {
- field->Set64(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->Set64<false>(field->GetDeclaringClass(), new_value);
return 0;
}
return -1;
@@ -67,13 +71,15 @@ extern "C" int32_t art_portable_set_obj_static_from_code(uint32_t field_idx,
mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectWrite,
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != NULL)) {
- field->SetObj(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->SetObj<false>(field->GetDeclaringClass(), new_value);
return 0;
}
field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, Thread::Current(),
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != NULL)) {
- field->SetObj(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->SetObj<false>(field->GetDeclaringClass(), new_value);
return 0;
}
return -1;
@@ -131,13 +137,15 @@ extern "C" int32_t art_portable_set32_instance_from_code(uint32_t field_idx,
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint32_t));
if (LIKELY(field != NULL)) {
- field->Set32(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->Set32<false>(obj, new_value);
return 0;
}
field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
sizeof(uint32_t));
if (LIKELY(field != NULL)) {
- field->Set32(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->Set32<false>(obj, new_value);
return 0;
}
return -1;
@@ -149,13 +157,15 @@ extern "C" int32_t art_portable_set64_instance_from_code(uint32_t field_idx,
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint64_t));
if (LIKELY(field != NULL)) {
- field->Set64(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->Set64<false>(obj, new_value);
return 0;
}
field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
sizeof(uint64_t));
if (LIKELY(field != NULL)) {
- field->Set64(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->Set64<false>(obj, new_value);
return 0;
}
return -1;
@@ -169,13 +179,15 @@ extern "C" int32_t art_portable_set_obj_instance_from_code(uint32_t field_idx,
mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite,
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != NULL)) {
- field->SetObj(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->SetObj<false>(obj, new_value);
return 0;
}
field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, Thread::Current(),
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != NULL)) {
- field->SetObj(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->SetObj<false>(obj, new_value);
return 0;
}
return -1;
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 93ff7aa..2d5c07d 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -154,13 +154,15 @@ extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value,
mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
sizeof(int32_t));
if (LIKELY(field != NULL)) {
- field->Set32(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->Set32<false>(field->GetDeclaringClass(), new_value);
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int32_t));
if (LIKELY(field != NULL)) {
- field->Set32(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->Set32<false>(field->GetDeclaringClass(), new_value);
return 0; // success
}
return -1; // failure
@@ -172,13 +174,15 @@ extern "C" int artSet64StaticFromCode(uint32_t field_idx, mirror::ArtMethod* ref
mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
sizeof(int64_t));
if (LIKELY(field != NULL)) {
- field->Set64(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->Set64<false>(field->GetDeclaringClass(), new_value);
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int64_t));
if (LIKELY(field != NULL)) {
- field->Set64(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->Set64<false>(field->GetDeclaringClass(), new_value);
return 0; // success
}
return -1; // failure
@@ -192,7 +196,8 @@ extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_v
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != NULL)) {
if (LIKELY(!FieldHelper(field).IsPrimitiveType())) {
- field->SetObj(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->SetObj<false>(field->GetDeclaringClass(), new_value);
return 0; // success
}
}
@@ -200,7 +205,8 @@ extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_v
field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, self,
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != NULL)) {
- field->SetObj(field->GetDeclaringClass(), new_value);
+ // Compiled code can't use transactional mode.
+ field->SetObj<false>(field->GetDeclaringClass(), new_value);
return 0; // success
}
return -1; // failure
@@ -213,7 +219,8 @@ extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
sizeof(int32_t));
if (LIKELY(field != NULL && obj != NULL)) {
- field->Set32(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->Set32<false>(obj, new_value);
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
@@ -224,7 +231,8 @@ extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
} else {
- field->Set32(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->Set32<false>(obj, new_value);
return 0; // success
}
}
@@ -240,7 +248,8 @@ extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
sizeof(int64_t));
if (LIKELY(field != NULL && obj != NULL)) {
- field->Set64(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->Set64<false>(obj, new_value);
return 0; // success
}
*sp = callee_save;
@@ -252,7 +261,8 @@ extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
} else {
- field->Set64(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->Set64<false>(obj, new_value);
return 0; // success
}
}
@@ -267,7 +277,8 @@ extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj
mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite,
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != NULL && obj != NULL)) {
- field->SetObj(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->SetObj<false>(obj, new_value);
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
@@ -278,7 +289,8 @@ extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
} else {
- field->SetObj(obj, new_value);
+ // Compiled code can't use transactional mode.
+ field->SetObj<false>(obj, new_value);
return 0; // success
}
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index f9486c3..012dabb 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -558,7 +558,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
// We came here because of sharpening. Ensure the dex cache is up-to-date on the method index
// of the sharpened method.
if (called->GetDexCacheResolvedMethods() == caller->GetDexCacheResolvedMethods()) {
- caller->GetDexCacheResolvedMethods()->Set(called->GetDexMethodIndex(), called);
+ caller->GetDexCacheResolvedMethods()->Set<false>(called->GetDexMethodIndex(), called);
} else {
// Calling from one dex file to another, need to compute the method index appropriate to
// the caller's dex file. Since we get here only if the original called was a runtime
@@ -567,7 +567,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
uint32_t method_index =
MethodHelper(called).FindDexMethodIndexInOtherDexFile(*dex_file, dex_method_idx);
if (method_index != DexFile::kDexNoIndex) {
- caller->GetDexCacheResolvedMethods()->Set(method_index, called);
+ caller->GetDexCacheResolvedMethods()->Set<false>(method_index, called);
}
}
}
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index aad214a..6258070 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -84,7 +84,11 @@ class ModUnionUpdateObjectReferencesVisitor {
if (new_ref != ref) {
// Use SetFieldObjectWithoutWriteBarrier to avoid card mark as an optimization which
// reduces dirtied pages and improves performance.
- obj->SetFieldObjectWithoutWriteBarrier(offset, new_ref, true);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->SetFieldObjectWithoutWriteBarrier<true>(offset, new_ref, true);
+ } else {
+ obj->SetFieldObjectWithoutWriteBarrier<false>(offset, new_ref, true);
+ }
}
}
}
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index b1122b9..6d9496e 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -662,7 +662,9 @@ void SemiSpace::ScanObject(Object* obj) {
// Don't need to mark the card since we updating the object address and not changing the
// actual objects its pointing to. Using SetFieldObjectWithoutWriteBarrier is better in this
// case since it does not dirty cards and use additional memory.
- obj->SetFieldObjectWithoutWriteBarrier(offset, new_address, false);
+ // Since we do not change the actual object, we can safely use non-transactional mode. Also
+ // disable check as we could run inside a transaction.
+ obj->SetFieldObjectWithoutWriteBarrier<false, false>(offset, new_address, false);
}
}, kMovingClasses);
mirror::Class* klass = obj->GetClass();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index f1126ef..7613a31 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2335,7 +2335,7 @@ void Heap::SetReferenceOffsets(MemberOffset reference_referent_offset,
void Heap::SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) {
DCHECK(reference != NULL);
DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U);
- reference->SetFieldObject(reference_referent_offset_, referent, true);
+ reference->SetFieldObject<false, false>(reference_referent_offset_, referent, true);
}
mirror::Object* Heap::GetReferenceReferent(mirror::Object* reference) {
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index 8af2725..b02b8bb 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -50,7 +50,7 @@ TEST_F(HeapTest, GarbageCollectClassLinkerInit) {
for (size_t j = 0; j < 2048; ++j) {
mirror::String* string = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!");
// SIRT operator -> deferences the SIRT before running the method.
- array->Set(j, string);
+ array->Set<false>(j, string);
}
}
}
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index fae4cac..203701f 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -49,12 +49,21 @@ void ReferenceQueue::EnqueuePendingReference(mirror::Object* ref) {
DCHECK_NE(pending_next_offset.Uint32Value(), 0U);
if (IsEmpty()) {
// 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref;
- ref->SetFieldObject(pending_next_offset, ref, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ ref->SetFieldObject<true>(pending_next_offset, ref, false);
+ } else {
+ ref->SetFieldObject<false>(pending_next_offset, ref, false);
+ }
list_ = ref;
} else {
mirror::Object* head = list_->GetFieldObject<mirror::Object>(pending_next_offset, false);
- ref->SetFieldObject(pending_next_offset, head, false);
- list_->SetFieldObject(pending_next_offset, ref, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ ref->SetFieldObject<true>(pending_next_offset, head, false);
+ list_->SetFieldObject<true>(pending_next_offset, ref, false);
+ } else {
+ ref->SetFieldObject<false>(pending_next_offset, head, false);
+ list_->SetFieldObject<false>(pending_next_offset, ref, false);
+ }
}
}
@@ -71,10 +80,18 @@ mirror::Object* ReferenceQueue::DequeuePendingReference() {
list_ = nullptr;
} else {
mirror::Object* next = head->GetFieldObject<mirror::Object>(pending_next_offset, false);
- list_->SetFieldObject(pending_next_offset, next, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ list_->SetFieldObject<true>(pending_next_offset, next, false);
+ } else {
+ list_->SetFieldObject<false>(pending_next_offset, next, false);
+ }
ref = head;
}
- ref->SetFieldObject(pending_next_offset, nullptr, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ ref->SetFieldObject<true>(pending_next_offset, nullptr, false);
+ } else {
+ ref->SetFieldObject<false>(pending_next_offset, nullptr, false);
+ }
return ref;
}
@@ -131,7 +148,11 @@ void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue& cleared_referenc
// If the referent is non-null the reference must queuable.
DCHECK(heap_->IsEnqueuable(ref));
// Move the updated referent to the zombie field.
- ref->SetFieldObject(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ ref->SetFieldObject<true>(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false);
+ } else {
+ ref->SetFieldObject<false>(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false);
+ }
heap_->ClearReferenceReferent(ref);
cleared_references.EnqueueReference(ref);
} else if (referent != forward_address) {
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 5693747..cc49d67 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -28,24 +28,24 @@
namespace art {
InternTable::InternTable()
- : intern_table_lock_("InternTable lock"), is_dirty_(false), allow_new_interns_(true),
- new_intern_condition_("New intern condition", intern_table_lock_) {
+ : is_dirty_(false), allow_new_interns_(true),
+ new_intern_condition_("New intern condition", *Locks::intern_table_lock_) {
}
size_t InternTable::Size() const {
- MutexLock mu(Thread::Current(), intern_table_lock_);
+ MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
return strong_interns_.size() + weak_interns_.size();
}
void InternTable::DumpForSigQuit(std::ostream& os) const {
- MutexLock mu(Thread::Current(), intern_table_lock_);
+ MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
os << "Intern table: " << strong_interns_.size() << " strong; "
<< weak_interns_.size() << " weak\n";
}
void InternTable::VisitRoots(RootCallback* callback, void* arg,
bool only_dirty, bool clean_dirty) {
- MutexLock mu(Thread::Current(), intern_table_lock_);
+ MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
if (!only_dirty || is_dirty_) {
for (auto& strong_intern : strong_interns_) {
strong_intern.second =
@@ -61,7 +61,7 @@ void InternTable::VisitRoots(RootCallback* callback, void* arg,
}
mirror::String* InternTable::Lookup(Table& table, mirror::String* s, uint32_t hash_code) {
- intern_table_lock_.AssertHeld(Thread::Current());
+ Locks::intern_table_lock_->AssertHeld(Thread::Current());
for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) {
mirror::String* existing_string = it->second;
if (existing_string->Equals(s)) {
@@ -71,15 +71,38 @@ mirror::String* InternTable::Lookup(Table& table, mirror::String* s, uint32_t ha
return NULL;
}
+mirror::String* InternTable::InsertStrong(mirror::String* s, uint32_t hash_code) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->IsActiveTransaction()) {
+ runtime->RecordStrongStringInsertion(s, hash_code);
+ }
+ return Insert(strong_interns_, s, hash_code);
+}
+
+mirror::String* InternTable::InsertWeak(mirror::String* s, uint32_t hash_code) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->IsActiveTransaction()) {
+ runtime->RecordWeakStringInsertion(s, hash_code);
+ }
+ return Insert(weak_interns_, s, hash_code);
+}
+
mirror::String* InternTable::Insert(Table& table, mirror::String* s, uint32_t hash_code) {
- intern_table_lock_.AssertHeld(Thread::Current());
+ Locks::intern_table_lock_->AssertHeld(Thread::Current());
table.insert(std::make_pair(hash_code, s));
return s;
}
-void InternTable::Remove(Table& table, const mirror::String* s,
- uint32_t hash_code) {
- intern_table_lock_.AssertHeld(Thread::Current());
+void InternTable::RemoveWeak(mirror::String* s, uint32_t hash_code) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->IsActiveTransaction()) {
+ runtime->RecordWeakStringRemoval(s, hash_code);
+ }
+ Remove(weak_interns_, s, hash_code);
+}
+
+void InternTable::Remove(Table& table, mirror::String* s, uint32_t hash_code) {
+ Locks::intern_table_lock_->AssertHeld(Thread::Current());
for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) {
if (it->second == s) {
table.erase(it);
@@ -88,6 +111,24 @@ void InternTable::Remove(Table& table, const mirror::String* s,
}
}
+// Insert/remove methods used to undo changes made during an aborted transaction.
+mirror::String* InternTable::InsertStrongFromTransaction(mirror::String* s, uint32_t hash_code) {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ return InsertStrong(s, hash_code);
+}
+mirror::String* InternTable::InsertWeakFromTransaction(mirror::String* s, uint32_t hash_code) {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ return InsertWeak(s, hash_code);
+}
+void InternTable::RemoveStrongFromTransaction(mirror::String* s, uint32_t hash_code) {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ Remove(strong_interns_, s, hash_code);
+}
+void InternTable::RemoveWeakFromTransaction(mirror::String* s, uint32_t hash_code) {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ Remove(weak_interns_, s, hash_code);
+}
+
static mirror::String* LookupStringFromImage(mirror::String* s)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace();
@@ -115,20 +156,20 @@ static mirror::String* LookupStringFromImage(mirror::String* s)
void InternTable::AllowNewInterns() {
Thread* self = Thread::Current();
- MutexLock mu(self, intern_table_lock_);
+ MutexLock mu(self, *Locks::intern_table_lock_);
allow_new_interns_ = true;
new_intern_condition_.Broadcast(self);
}
void InternTable::DisallowNewInterns() {
Thread* self = Thread::Current();
- MutexLock mu(self, intern_table_lock_);
+ MutexLock mu(self, *Locks::intern_table_lock_);
allow_new_interns_ = false;
}
mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
Thread* self = Thread::Current();
- MutexLock mu(self, intern_table_lock_);
+ MutexLock mu(self, *Locks::intern_table_lock_);
DCHECK(s != NULL);
uint32_t hash_code = s->GetHashCode();
@@ -150,20 +191,20 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
// Check the image for a match.
mirror::String* image = LookupStringFromImage(s);
if (image != NULL) {
- return Insert(strong_interns_, image, hash_code);
+ return InsertStrong(image, hash_code);
}
// There is no match in the strong table, check the weak table.
mirror::String* weak = Lookup(weak_interns_, s, hash_code);
if (weak != NULL) {
// A match was found in the weak table. Promote to the strong table.
- Remove(weak_interns_, weak, hash_code);
- return Insert(strong_interns_, weak, hash_code);
+ RemoveWeak(weak, hash_code);
+ return InsertStrong(weak, hash_code);
}
// No match in the strong table or the weak table. Insert into the strong
// table.
- return Insert(strong_interns_, s, hash_code);
+ return InsertStrong(s, hash_code);
}
// Check the strong table for a match.
@@ -174,7 +215,7 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
// Check the image for a match.
mirror::String* image = LookupStringFromImage(s);
if (image != NULL) {
- return Insert(weak_interns_, image, hash_code);
+ return InsertWeak(image, hash_code);
}
// Check the weak table for a match.
mirror::String* weak = Lookup(weak_interns_, s, hash_code);
@@ -182,7 +223,7 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
return weak;
}
// Insert into the weak table.
- return Insert(weak_interns_, s, hash_code);
+ return InsertWeak(s, hash_code);
}
mirror::String* InternTable::InternStrong(int32_t utf16_length,
@@ -211,13 +252,13 @@ mirror::String* InternTable::InternWeak(mirror::String* s) {
}
bool InternTable::ContainsWeak(mirror::String* s) {
- MutexLock mu(Thread::Current(), intern_table_lock_);
+ MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
const mirror::String* found = Lookup(weak_interns_, s, s->GetHashCode());
return found == s;
}
void InternTable::SweepInternTableWeaks(IsMarkedCallback* callback, void* arg) {
- MutexLock mu(Thread::Current(), intern_table_lock_);
+ MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) {
mirror::Object* object = it->second;
mirror::Object* new_object = callback(object, arg);
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 9f09fb9..cc48480 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_INTERN_TABLE_H_
#include "base/mutex.h"
+#include "locks.h"
#include "object_callbacks.h"
#include <map>
@@ -26,6 +27,7 @@ namespace art {
namespace mirror {
class String;
} // namespace mirror
+class Transaction;
/**
* Used to intern strings.
@@ -72,19 +74,38 @@ class InternTable {
typedef std::multimap<int32_t, mirror::String*> Table;
mirror::String* Insert(mirror::String* s, bool is_strong)
+ LOCKS_EXCLUDED(Locks::intern_table_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::String* Lookup(Table& table, mirror::String* s, uint32_t hash_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::String* Insert(Table& table, mirror::String* s, uint32_t hash_code);
- void Remove(Table& table, const mirror::String* s, uint32_t hash_code);
-
- mutable Mutex intern_table_lock_;
- bool is_dirty_ GUARDED_BY(intern_table_lock_);
- bool allow_new_interns_ GUARDED_BY(intern_table_lock_);
- ConditionVariable new_intern_condition_ GUARDED_BY(intern_table_lock_);
- Table strong_interns_ GUARDED_BY(intern_table_lock_);
- Table weak_interns_ GUARDED_BY(intern_table_lock_);
+ mirror::String* InsertStrong(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ mirror::String* InsertWeak(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ mirror::String* Insert(Table& table, mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void RemoveWeak(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void Remove(Table& table, mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+
+ // Transaction rollback access.
+ mirror::String* InsertStrongFromTransaction(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ mirror::String* InsertWeakFromTransaction(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void RemoveStrongFromTransaction(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void RemoveWeakFromTransaction(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ friend class Transaction;
+
+ bool is_dirty_ GUARDED_BY(Locks::intern_table_lock_);
+ bool allow_new_interns_ GUARDED_BY(Locks::intern_table_lock_);
+ ConditionVariable new_intern_condition_ GUARDED_BY(Locks::intern_table_lock_);
+ Table strong_interns_ GUARDED_BY(Locks::intern_table_lock_);
+ Table weak_interns_ GUARDED_BY(Locks::intern_table_lock_);
};
} // namespace art
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index c6faf44..a674571 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -15,6 +15,7 @@
*/
#include "interpreter_common.h"
+#include <limits>
namespace art {
namespace interpreter {
@@ -23,6 +24,10 @@ namespace interpreter {
static void UnstartedRuntimeJni(Thread* self, ArtMethod* method,
Object* receiver, uint32_t* args, JValue* result)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(Runtime::Current()->IsActiveTransaction()) << "Calling native method "
+ << PrettyMethod(method)
+ << " in unstarted runtime should only happen"
+ << " in a transaction";
std::string name(PrettyMethod(method));
if (name == "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") {
result->SetL(NULL);
@@ -73,13 +78,18 @@ static void UnstartedRuntimeJni(Thread* self, ArtMethod* method,
jint newValue = args[4];
byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr);
+ // Check offset is 32bits to fit in MemberOffset.
+ CHECK_GE(offset, static_cast<jlong>(std::numeric_limits<int32_t>::min()));
+ CHECK_LE(offset, static_cast<jlong>(std::numeric_limits<int32_t>::max()));
+ Runtime::Current()->RecordWriteField32(obj, MemberOffset(offset), *address, true);
// Note: android_atomic_release_cas() returns 0 on success, not failure.
int r = android_atomic_release_cas(expectedValue, newValue, address);
result->SetZ(r == 0);
} else if (name == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") {
Object* obj = reinterpret_cast<Object*>(args[0]);
Object* newValue = reinterpret_cast<Object*>(args[3]);
- obj->SetFieldObject(MemberOffset((static_cast<uint64_t>(args[2]) << 32) | args[1]), newValue, false);
+ obj->SetFieldObject<true>(MemberOffset((static_cast<uint64_t>(args[2]) << 32) | args[1]),
+ newValue, false);
} else if (name == "int sun.misc.Unsafe.getArrayBaseOffsetForComponentType(java.lang.Class)") {
mirror::Class* component = reinterpret_cast<Object*>(args[0])->AsClass();
Primitive::Type primitive_type = component->GetPrimitiveType();
@@ -89,7 +99,11 @@ static void UnstartedRuntimeJni(Thread* self, ArtMethod* method,
Primitive::Type primitive_type = component->GetPrimitiveType();
result->SetI(Primitive::ComponentSize(primitive_type));
} else {
- LOG(FATAL) << "Attempt to invoke native method in non-started runtime: " << name;
+ // Throw an exception so we can abort the transaction and undo every change.
+ ThrowLocation throw_location;
+ self->ThrowNewExceptionF(throw_location, "Ljava/lang/InternalError;",
+ "Attempt to invoke native method in non-started runtime: %s",
+ name.c_str());
}
}
@@ -293,21 +307,38 @@ static inline JValue Execute(Thread* self, MethodHelper& mh, const DexFile::Code
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
+ bool transaction_active = Runtime::Current()->IsActiveTransaction();
if (LIKELY(shadow_frame.GetMethod()->IsPreverified())) {
// Enter the "without access check" interpreter.
if (kInterpreterImplKind == kSwitchImpl) {
- return ExecuteSwitchImpl<false>(self, mh, code_item, shadow_frame, result_register);
+ if (transaction_active) {
+ return ExecuteSwitchImpl<false, true>(self, mh, code_item, shadow_frame, result_register);
+ } else {
+ return ExecuteSwitchImpl<false, false>(self, mh, code_item, shadow_frame, result_register);
+ }
} else {
DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
- return ExecuteGotoImpl<false>(self, mh, code_item, shadow_frame, result_register);
+ if (transaction_active) {
+ return ExecuteGotoImpl<false, true>(self, mh, code_item, shadow_frame, result_register);
+ } else {
+ return ExecuteGotoImpl<false, false>(self, mh, code_item, shadow_frame, result_register);
+ }
}
} else {
// Enter the "with access check" interpreter.
if (kInterpreterImplKind == kSwitchImpl) {
- return ExecuteSwitchImpl<true>(self, mh, code_item, shadow_frame, result_register);
+ if (transaction_active) {
+ return ExecuteSwitchImpl<true, true>(self, mh, code_item, shadow_frame, result_register);
+ } else {
+ return ExecuteSwitchImpl<true, false>(self, mh, code_item, shadow_frame, result_register);
+ }
} else {
DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
- return ExecuteGotoImpl<true>(self, mh, code_item, shadow_frame, result_register);
+ if (transaction_active) {
+ return ExecuteGotoImpl<true, true>(self, mh, code_item, shadow_frame, result_register);
+ } else {
+ return ExecuteGotoImpl<true, false>(self, mh, code_item, shadow_frame, result_register);
+ }
}
}
}
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 0b959fb..e37fb61 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -15,6 +15,7 @@
*/
#include "interpreter_common.h"
+#include "mirror/array-inl.h"
namespace art {
namespace interpreter {
@@ -161,7 +162,7 @@ bool DoCall(ArtMethod* method, Thread* self, ShadowFrame& shadow_frame,
return !self->IsExceptionPending();
}
-template <bool is_range, bool do_access_check>
+template <bool is_range, bool do_access_check, bool transaction_active>
bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
Thread* self, JValue* result) {
DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY ||
@@ -212,9 +213,9 @@ bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
for (int32_t i = 0; i < length; ++i) {
size_t src_reg = is_range ? vregC + i : arg[i];
if (is_primitive_int_component) {
- newArray->AsIntArray()->SetWithoutChecks(i, shadow_frame.GetVReg(src_reg));
+ newArray->AsIntArray()->SetWithoutChecks<transaction_active>(i, shadow_frame.GetVReg(src_reg));
} else {
- newArray->AsObjectArray<Object>()->SetWithoutChecks(i, shadow_frame.GetVRegReference(src_reg));
+ newArray->AsObjectArray<Object>()->SetWithoutChecks<transaction_active>(i, shadow_frame.GetVRegReference(src_reg));
}
}
@@ -222,6 +223,50 @@ bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
return true;
}
+// TODO fix thread analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+template<typename T>
+static void RecordArrayElementsInTransactionImpl(mirror::PrimitiveArray<T>* array, int32_t count)
+ NO_THREAD_SAFETY_ANALYSIS {
+ Runtime* runtime = Runtime::Current();
+ for (int32_t i = 0; i < count; ++i) {
+ runtime->RecordWriteArray(array, i, array->GetWithoutChecks(i));
+ }
+}
+
+void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(Runtime::Current()->IsActiveTransaction());
+ DCHECK(array != nullptr);
+ DCHECK_LE(count, array->GetLength());
+ Primitive::Type primitive_component_type = array->GetClass()->GetComponentType()->GetPrimitiveType();
+ switch (primitive_component_type) {
+ case Primitive::kPrimBoolean:
+ RecordArrayElementsInTransactionImpl(array->AsBooleanArray(), count);
+ break;
+ case Primitive::kPrimByte:
+ RecordArrayElementsInTransactionImpl(array->AsByteArray(), count);
+ break;
+ case Primitive::kPrimChar:
+ RecordArrayElementsInTransactionImpl(array->AsCharArray(), count);
+ break;
+ case Primitive::kPrimShort:
+ RecordArrayElementsInTransactionImpl(array->AsShortArray(), count);
+ break;
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ RecordArrayElementsInTransactionImpl(array->AsIntArray(), count);
+ break;
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ RecordArrayElementsInTransactionImpl(array->AsLongArray(), count);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported primitive type " << primitive_component_type
+ << " in fill-array-data";
+ break;
+ }
+}
+
static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
JValue* result, size_t arg_offset) {
@@ -341,15 +386,19 @@ EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
#undef EXPLICIT_DO_CALL_TEMPLATE_DECL
// Explicit DoFilledNewArray template function declarations.
-#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check) \
- template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
- bool DoFilledNewArray<_is_range_, _check>(const Instruction* inst, \
- const ShadowFrame& shadow_frame, \
- Thread* self, JValue* result)
-EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false);
-EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true);
+#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
+ bool DoFilledNewArray<_is_range_, _check, _transaction_active>(const Instruction* inst, \
+ const ShadowFrame& shadow_frame, \
+ Thread* self, JValue* result)
+#define EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(_transaction_active) \
+ EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false, _transaction_active); \
+ EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true, _transaction_active); \
+ EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false, _transaction_active); \
+ EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true, _transaction_active)
+EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(false);
+EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(true);
+#undef EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL
} // namespace interpreter
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 768ca33..a03e420 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -65,12 +65,12 @@ namespace interpreter {
// External references to both interpreter implementations.
-template<bool do_access_check>
+template<bool do_access_check, bool transaction_active>
extern JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register);
-template<bool do_access_check>
+template<bool do_access_check, bool transaction_active>
extern JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register);
@@ -83,6 +83,9 @@ static inline void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANA
ref->MonitorExit(self);
}
+void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Invokes the given method. This is part of the invocation support and is used by DoInvoke and
// DoInvokeVirtualQuick functions.
// Returns true on success, otherwise throws an exception and returns false.
@@ -228,7 +231,7 @@ static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* ins
// Handles iput-XXX and sput-XXX instructions.
// Returns true on success, otherwise throws an exception and returns false.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check, bool transaction_active>
static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
const Instruction* inst, uint16_t inst_data) {
bool do_assignability_check = do_access_check;
@@ -254,22 +257,22 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
switch (field_type) {
case Primitive::kPrimBoolean:
- f->SetBoolean(obj, shadow_frame.GetVReg(vregA));
+ f->SetBoolean<transaction_active>(obj, shadow_frame.GetVReg(vregA));
break;
case Primitive::kPrimByte:
- f->SetByte(obj, shadow_frame.GetVReg(vregA));
+ f->SetByte<transaction_active>(obj, shadow_frame.GetVReg(vregA));
break;
case Primitive::kPrimChar:
- f->SetChar(obj, shadow_frame.GetVReg(vregA));
+ f->SetChar<transaction_active>(obj, shadow_frame.GetVReg(vregA));
break;
case Primitive::kPrimShort:
- f->SetShort(obj, shadow_frame.GetVReg(vregA));
+ f->SetShort<transaction_active>(obj, shadow_frame.GetVReg(vregA));
break;
case Primitive::kPrimInt:
- f->SetInt(obj, shadow_frame.GetVReg(vregA));
+ f->SetInt<transaction_active>(obj, shadow_frame.GetVReg(vregA));
break;
case Primitive::kPrimLong:
- f->SetLong(obj, shadow_frame.GetVRegLong(vregA));
+ f->SetLong<transaction_active>(obj, shadow_frame.GetVRegLong(vregA));
break;
case Primitive::kPrimNot: {
Object* reg = shadow_frame.GetVRegReference(vregA);
@@ -286,7 +289,7 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
return false;
}
}
- f->SetObj(obj, reg);
+ f->SetObj<transaction_active>(obj, reg);
break;
}
default:
@@ -297,7 +300,7 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
// Handles iput-quick, iput-wide-quick and iput-object-quick instructions.
// Returns true on success, otherwise throws an exception and returns false.
-template<Primitive::Type field_type>
+template<Primitive::Type field_type, bool transaction_active>
static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) {
Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
if (UNLIKELY(obj == nullptr)) {
@@ -311,13 +314,15 @@ static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instructio
const uint32_t vregA = inst->VRegA_22c(inst_data);
switch (field_type) {
case Primitive::kPrimInt:
- obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
+ obj->SetField32<transaction_active>(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
break;
case Primitive::kPrimLong:
- obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile);
+ obj->SetField64<transaction_active>(field_offset, shadow_frame.GetVRegLong(vregA),
+ is_volatile);
break;
case Primitive::kPrimNot:
- obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile);
+ obj->SetFieldObject<transaction_active>(field_offset, shadow_frame.GetVRegReference(vregA),
+ is_volatile);
break;
default:
LOG(FATAL) << "Unreachable: " << field_type;
@@ -416,7 +421,7 @@ static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg,
// Handles filled-new-array and filled-new-array-range instructions.
// Returns true on success, otherwise throws an exception and returns false.
-template <bool is_range, bool do_access_check>
+template <bool is_range, bool do_access_check, bool transaction_active>
bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
Thread* self, JValue* result);
@@ -604,14 +609,16 @@ EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot);
#undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL
// Explicitly instantiate all DoFieldPut functions.
-#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check) \
+#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check, _transaction_active) \
template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
- bool DoFieldPut<_find_type, _field_type, _do_check>(Thread* self, const ShadowFrame& shadow_frame, \
- const Instruction* inst, uint16_t inst_data)
+ bool DoFieldPut<_find_type, _field_type, _do_check, _transaction_active>(Thread* self, const ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data)
#define EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(_find_type, _field_type) \
- EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false); \
- EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true);
+ EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false, false); \
+ EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true, false); \
+ EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false, true); \
+ EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true, true);
// iput-XXX
EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimBoolean);
@@ -657,14 +664,20 @@ EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-qui
#undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL
// Explicitly instantiate all DoIPutQuick functions.
-#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type) \
- template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
- bool DoIPutQuick<_field_type>(const ShadowFrame& shadow_frame, const Instruction* inst, \
- uint16_t inst_data)
-
-EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick.
-EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick.
-EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick.
+#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, _transaction_active) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ bool DoIPutQuick<_field_type, _transaction_active>(const ShadowFrame& shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data)
+
+#define EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(_field_type) \
+ EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, false); \
+ EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, true);
+
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick.
+#undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
} // namespace interpreter
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index e8504b7..d0bb001 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -109,7 +109,7 @@ namespace interpreter {
* ---------------------+---------------+
*
*/
-template<bool do_access_check>
+template<bool do_access_check, bool transaction_active>
JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register) {
// Define handler tables:
@@ -536,15 +536,17 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY) {
- bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
- self, &result_register);
+ bool success =
+ DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame,
+ self, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY_RANGE) {
- bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
- self, &result_register);
+ bool success =
+ DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
+ self, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
}
HANDLE_INSTRUCTION_END();
@@ -567,6 +569,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
array->GetLength(), payload->element_count);
HANDLE_PENDING_EXCEPTION();
} else {
+ if (transaction_active) {
+ RecordArrayElementsInTransaction(array, payload->element_count);
+ }
uint32_t size_in_bytes = payload->element_count * payload->element_width;
memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes);
ADVANCE(3);
@@ -1060,7 +1065,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
BooleanArray* array = a->AsBooleanArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
ADVANCE(2);
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1079,7 +1084,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
ByteArray* array = a->AsByteArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
ADVANCE(2);
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1098,7 +1103,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
CharArray* array = a->AsCharArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
ADVANCE(2);
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1117,7 +1122,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
ShortArray* array = a->AsShortArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
ADVANCE(2);
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1136,7 +1141,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
IntArray* array = a->AsIntArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
ADVANCE(2);
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1155,7 +1160,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
LongArray* array = a->AsLongArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
ADVANCE(2);
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1174,7 +1179,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
ObjectArray<Object>* array = a->AsObjectArray<Object>();
if (LIKELY(array->CheckIsValidIndex(index) && array->CheckAssignable(val))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
ADVANCE(2);
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1286,103 +1291,103 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT_BOOLEAN) {
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT_BYTE) {
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT_CHAR) {
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT_SHORT) {
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT) {
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT_WIDE) {
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT_OBJECT) {
- bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT_QUICK) {
- bool success = DoIPutQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT_WIDE_QUICK) {
- bool success = DoIPutQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(IPUT_OBJECT_QUICK) {
- bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(SPUT_BOOLEAN) {
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(SPUT_BYTE) {
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(SPUT_CHAR) {
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(SPUT_SHORT) {
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(SPUT) {
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(SPUT_WIDE) {
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(SPUT_OBJECT) {
- bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
}
HANDLE_INSTRUCTION_END();
@@ -2390,13 +2395,21 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem*
// Explicit definitions of ExecuteGotoImpl.
template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
-JValue ExecuteGotoImpl<true>(Thread* self, MethodHelper& mh,
- const DexFile::CodeItem* code_item,
- ShadowFrame& shadow_frame, JValue result_register);
+JValue ExecuteGotoImpl<true, false>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
-JValue ExecuteGotoImpl<false>(Thread* self, MethodHelper& mh,
- const DexFile::CodeItem* code_item,
- ShadowFrame& shadow_frame, JValue result_register);
+JValue ExecuteGotoImpl<false, false>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+JValue ExecuteGotoImpl<true, true>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+JValue ExecuteGotoImpl<false, true>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
} // namespace interpreter
} // namespace art
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index e5d15b1..abee1db 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -50,7 +50,7 @@ namespace interpreter {
// Code to run before each dex instruction.
#define PREAMBLE()
-template<bool do_access_check>
+template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register) {
bool do_assignability_check = do_access_check;
@@ -449,15 +449,17 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
}
case Instruction::FILLED_NEW_ARRAY: {
PREAMBLE();
- bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
- self, &result_register);
+ bool success =
+ DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self,
+ &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
break;
}
case Instruction::FILLED_NEW_ARRAY_RANGE: {
PREAMBLE();
- bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
- self, &result_register);
+ bool success =
+ DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
+ self, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
break;
}
@@ -482,6 +484,9 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
HANDLE_PENDING_EXCEPTION();
break;
}
+ if (transaction_active) {
+ RecordArrayElementsInTransaction(array, payload->element_count);
+ }
uint32_t size_in_bytes = payload->element_count * payload->element_width;
memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes);
inst = inst->Next_3xx();
@@ -958,7 +963,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
BooleanArray* array = a->AsBooleanArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
} else {
HANDLE_PENDING_EXCEPTION();
@@ -977,7 +982,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
ByteArray* array = a->AsByteArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
} else {
HANDLE_PENDING_EXCEPTION();
@@ -996,7 +1001,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
CharArray* array = a->AsCharArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1015,7 +1020,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
ShortArray* array = a->AsShortArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1034,7 +1039,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
IntArray* array = a->AsIntArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1053,7 +1058,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
LongArray* array = a->AsLongArray();
if (LIKELY(array->CheckIsValidIndex(index))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1072,7 +1077,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
ObjectArray<Object>* array = a->AsObjectArray<Object>();
if (LIKELY(array->CheckIsValidIndex(index) && array->CheckAssignable(val))) {
- array->SetWithoutChecks(index, val);
+ array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
} else {
HANDLE_PENDING_EXCEPTION();
@@ -1183,103 +1188,103 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
}
case Instruction::IPUT_BOOLEAN: {
PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::IPUT_BYTE: {
PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::IPUT_CHAR: {
PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::IPUT_SHORT: {
PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::IPUT: {
PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::IPUT_WIDE: {
PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::IPUT_OBJECT: {
PREAMBLE();
- bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::IPUT_QUICK: {
PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::IPUT_WIDE_QUICK: {
PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::IPUT_OBJECT_QUICK: {
PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SPUT_BOOLEAN: {
PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SPUT_BYTE: {
PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SPUT_CHAR: {
PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SPUT_SHORT: {
PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SPUT: {
PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SPUT_WIDE: {
PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SPUT_OBJECT: {
PREAMBLE();
- bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
@@ -2137,13 +2142,21 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem
// Explicit definitions of ExecuteSwitchImpl.
template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
-JValue ExecuteSwitchImpl<true>(Thread* self, MethodHelper& mh,
- const DexFile::CodeItem* code_item,
- ShadowFrame& shadow_frame, JValue result_register);
+JValue ExecuteSwitchImpl<true, false>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
-JValue ExecuteSwitchImpl<false>(Thread* self, MethodHelper& mh,
- const DexFile::CodeItem* code_item,
- ShadowFrame& shadow_frame, JValue result_register);
+JValue ExecuteSwitchImpl<false, false>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+JValue ExecuteSwitchImpl<true, true>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+JValue ExecuteSwitchImpl<false, true>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
} // namespace interpreter
} // namespace art
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index f9bcc71..876de05 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -1551,7 +1551,7 @@ class JNI {
mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
mirror::Object* v = soa.Decode<mirror::Object*>(java_value);
mirror::ArtField* f = soa.DecodeField(fid);
- f->SetObject(o, v);
+ f->SetObject<false>(o, v);
}
static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) {
@@ -1559,7 +1559,7 @@ class JNI {
ScopedObjectAccess soa(env);
mirror::Object* v = soa.Decode<mirror::Object*>(java_value);
mirror::ArtField* f = soa.DecodeField(fid);
- f->SetObject(f->GetDeclaringClass(), v);
+ f->SetObject<false>(f->GetDeclaringClass(), v);
}
#define GET_PRIMITIVE_FIELD(fn, instance) \
@@ -1582,13 +1582,13 @@ class JNI {
ScopedObjectAccess soa(env); \
mirror::Object* o = soa.Decode<mirror::Object*>(instance); \
mirror::ArtField* f = soa.DecodeField(fid); \
- f->Set ##fn(o, value)
+ f->Set ##fn <false>(o, value)
#define SET_STATIC_PRIMITIVE_FIELD(fn, value) \
CHECK_NON_NULL_ARGUMENT(SetStatic #fn Field, fid); \
ScopedObjectAccess soa(env); \
mirror::ArtField* f = soa.DecodeField(fid); \
- f->Set ##fn(f->GetDeclaringClass(), value)
+ f->Set ##fn <false>(f->GetDeclaringClass(), value)
static jboolean GetBooleanField(JNIEnv* env, jobject obj, jfieldID fid) {
GET_PRIMITIVE_FIELD(Boolean, obj);
@@ -2085,7 +2085,7 @@ class JNI {
mirror::ObjectArray<mirror::Object>* array =
soa.Decode<mirror::ObjectArray<mirror::Object>*>(java_array);
mirror::Object* value = soa.Decode<mirror::Object*>(java_value);
- array->Set(index, value);
+ array->Set<false>(index, value);
}
static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) {
@@ -2166,7 +2166,7 @@ class JNI {
} else {
for (jsize i = 0; i < length; ++i) {
- result->SetWithoutChecks(i, initial_object);
+ result->SetWithoutChecks<false>(i, initial_object);
}
}
}
diff --git a/runtime/locks.cc b/runtime/locks.cc
index d08206a..246e339 100644
--- a/runtime/locks.cc
+++ b/runtime/locks.cc
@@ -33,6 +33,7 @@ Mutex* Locks::thread_suspend_count_lock_ = NULL;
Mutex* Locks::trace_lock_ = NULL;
Mutex* Locks::profiler_lock_ = NULL;
Mutex* Locks::unexpected_signal_lock_ = NULL;
+Mutex* Locks::intern_table_lock_ = NULL;
void Locks::Init() {
if (logging_lock_ != NULL) {
@@ -49,6 +50,7 @@ void Locks::Init() {
DCHECK(trace_lock_ != NULL);
DCHECK(profiler_lock_ != NULL);
DCHECK(unexpected_signal_lock_ != NULL);
+ DCHECK(intern_table_lock_ != NULL);
} else {
logging_lock_ = new Mutex("logging lock", kLoggingLock, true);
abort_lock_ = new Mutex("abort lock", kAbortLock, true);
@@ -76,6 +78,8 @@ void Locks::Init() {
profiler_lock_ = new Mutex("profiler lock", kProfilerLock);
DCHECK(unexpected_signal_lock_ == NULL);
unexpected_signal_lock_ = new Mutex("unexpected signal lock", kUnexpectedSignalLock, true);
+ DCHECK(intern_table_lock_ == NULL);
+ intern_table_lock_ = new Mutex("InternTable lock", kInternTableLock);
}
}
diff --git a/runtime/locks.h b/runtime/locks.h
index d4fbd91..2f9810d 100644
--- a/runtime/locks.h
+++ b/runtime/locks.h
@@ -44,6 +44,8 @@ enum LockLevel {
kDexFileMethodInlinerLock,
kDexFileToMethodInlinerMapLock,
kMarkSweepMarkStackLock,
+ kTransactionLogLock,
+ kInternTableLock,
kDefaultMutexLevel,
kMarkSweepLargeObjectLock,
kPinTableLock,
@@ -163,6 +165,9 @@ class Locks {
// doesn't try to hold a higher level Mutex.
#define DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(classlinker_classes_lock_)
+ // Guards intern table.
+ static Mutex* intern_table_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
+
// Have an exclusive aborting thread.
static Mutex* abort_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index c23234e..2180857 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -60,7 +60,8 @@ static Array* RecursiveCreateMultiArray(Thread* self,
CHECK(self->IsExceptionPending());
return nullptr;
}
- new_array->AsObjectArray<Array>()->Set(i, sub_array);
+ // Use non-transactional mode without check.
+ new_array->AsObjectArray<Array>()->Set<false, false>(i, sub_array);
}
}
return new_array.get();
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 2e123ef..7555975 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -20,6 +20,7 @@
#include "object.h"
#include "object_callbacks.h"
#include "gc/heap.h"
+#include "runtime.h"
#include "thread.h"
namespace art {
@@ -60,7 +61,9 @@ class MANAGED Array : public Object {
void SetLength(int32_t length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CHECK_GE(length, 0);
- SetField32(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false, false);
+ // We use non transactional version since we can't undo this write. We also disable checking
+ // since it would fail during a transaction.
+ SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false, false);
}
static MemberOffset LengthOffset() {
@@ -144,14 +147,34 @@ class MANAGED PrimitiveArray : public Array {
}
void Set(int32_t i, T value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ Set<true>(i, value);
+ } else {
+ Set<false>(i, value);
+ }
+ }
+
+ // TODO fix thread safety analysis broken by the use of template. This should be
+ // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+ template<bool kTransactionActive, bool kCheckTransaction = true>
+ void Set(int32_t i, T value) NO_THREAD_SAFETY_ANALYSIS {
if (LIKELY(CheckIsValidIndex(i))) {
- SetWithoutChecks(i, value);
+ SetWithoutChecks<kTransactionActive, kCheckTransaction>(i, value);
} else {
DCHECK(Thread::Current()->IsExceptionPending());
}
}
- void SetWithoutChecks(int32_t i, T value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // TODO fix thread safety analysis broken by the use of template. This should be
+ // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+ template<bool kTransactionActive, bool kCheckTransaction = true>
+ void SetWithoutChecks(int32_t i, T value) NO_THREAD_SAFETY_ANALYSIS {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteArray(this, i, GetWithoutChecks(i));
+ }
DCHECK(CheckIsValidIndex(i));
GetData()[i] = value;
}
diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h
index 530226b..6253edd 100644
--- a/runtime/mirror/art_field-inl.h
+++ b/runtime/mirror/art_field-inl.h
@@ -37,7 +37,8 @@ inline Class* ArtField::GetDeclaringClass() {
}
inline void ArtField::SetDeclaringClass(Class *new_declaring_class) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtField, declaring_class_), new_declaring_class, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, declaring_class_),
+ new_declaring_class, false);
}
inline uint32_t ArtField::GetAccessFlags() {
@@ -61,10 +62,11 @@ inline uint32_t ArtField::Get32(Object* object) {
return object->GetField32(GetOffset(), IsVolatile());
}
+template<bool kTransactionActive>
inline void ArtField::Set32(Object* object, uint32_t new_value) {
DCHECK(object != NULL) << PrettyField(this);
DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted());
- object->SetField32(GetOffset(), new_value, IsVolatile());
+ object->SetField32<kTransactionActive>(GetOffset(), new_value, IsVolatile());
}
inline uint64_t ArtField::Get64(Object* object) {
@@ -73,10 +75,11 @@ inline uint64_t ArtField::Get64(Object* object) {
return object->GetField64(GetOffset(), IsVolatile());
}
+template<bool kTransactionActive>
inline void ArtField::Set64(Object* object, uint64_t new_value) {
DCHECK(object != NULL) << PrettyField(this);
DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted());
- object->SetField64(GetOffset(), new_value, IsVolatile());
+ object->SetField64<kTransactionActive>(GetOffset(), new_value, IsVolatile());
}
inline Object* ArtField::GetObj(Object* object) {
@@ -85,10 +88,11 @@ inline Object* ArtField::GetObj(Object* object) {
return object->GetFieldObject<Object>(GetOffset(), IsVolatile());
}
+template<bool kTransactionActive>
inline void ArtField::SetObj(Object* object, Object* new_value) {
DCHECK(object != NULL) << PrettyField(this);
DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted());
- object->SetFieldObject(GetOffset(), new_value, IsVolatile());
+ object->SetFieldObject<kTransactionActive>(GetOffset(), new_value, IsVolatile());
}
inline bool ArtField::GetBoolean(Object* object) {
@@ -97,10 +101,11 @@ inline bool ArtField::GetBoolean(Object* object) {
return Get32(object);
}
+template<bool kTransactionActive>
inline void ArtField::SetBoolean(Object* object, bool z) {
DCHECK_EQ(Primitive::kPrimBoolean, FieldHelper(this).GetTypeAsPrimitiveType())
<< PrettyField(this);
- Set32(object, z);
+ Set32<kTransactionActive>(object, z);
}
inline int8_t ArtField::GetByte(Object* object) {
@@ -109,10 +114,11 @@ inline int8_t ArtField::GetByte(Object* object) {
return Get32(object);
}
+template<bool kTransactionActive>
inline void ArtField::SetByte(Object* object, int8_t b) {
DCHECK_EQ(Primitive::kPrimByte, FieldHelper(this).GetTypeAsPrimitiveType())
<< PrettyField(this);
- Set32(object, b);
+ Set32<kTransactionActive>(object, b);
}
inline uint16_t ArtField::GetChar(Object* object) {
@@ -121,10 +127,11 @@ inline uint16_t ArtField::GetChar(Object* object) {
return Get32(object);
}
+template<bool kTransactionActive>
inline void ArtField::SetChar(Object* object, uint16_t c) {
DCHECK_EQ(Primitive::kPrimChar, FieldHelper(this).GetTypeAsPrimitiveType())
<< PrettyField(this);
- Set32(object, c);
+ Set32<kTransactionActive>(object, c);
}
inline int16_t ArtField::GetShort(Object* object) {
@@ -133,42 +140,45 @@ inline int16_t ArtField::GetShort(Object* object) {
return Get32(object);
}
+template<bool kTransactionActive>
inline void ArtField::SetShort(Object* object, int16_t s) {
DCHECK_EQ(Primitive::kPrimShort, FieldHelper(this).GetTypeAsPrimitiveType())
<< PrettyField(this);
- Set32(object, s);
+ Set32<kTransactionActive>(object, s);
}
inline int32_t ArtField::GetInt(Object* object) {
-#ifndef NDEBUG
- Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
- CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
-#endif
+ if (kIsDebugBuild) {
+ Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+ CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
+ }
return Get32(object);
}
+template<bool kTransactionActive>
inline void ArtField::SetInt(Object* object, int32_t i) {
-#ifndef NDEBUG
- Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
- CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
-#endif
- Set32(object, i);
+ if (kIsDebugBuild) {
+ Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+ CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
+ }
+ Set32<kTransactionActive>(object, i);
}
inline int64_t ArtField::GetLong(Object* object) {
-#ifndef NDEBUG
- Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
- CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
-#endif
+ if (kIsDebugBuild) {
+ Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+ CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
+ }
return Get64(object);
}
+template<bool kTransactionActive>
inline void ArtField::SetLong(Object* object, int64_t j) {
-#ifndef NDEBUG
- Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
- CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
-#endif
- Set64(object, j);
+ if (kIsDebugBuild) {
+ Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+ CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
+ }
+ Set64<kTransactionActive>(object, j);
}
inline float ArtField::GetFloat(Object* object) {
@@ -179,12 +189,13 @@ inline float ArtField::GetFloat(Object* object) {
return bits.GetF();
}
+template<bool kTransactionActive>
inline void ArtField::SetFloat(Object* object, float f) {
DCHECK_EQ(Primitive::kPrimFloat, FieldHelper(this).GetTypeAsPrimitiveType())
<< PrettyField(this);
JValue bits;
bits.SetF(f);
- Set32(object, bits.GetI());
+ Set32<kTransactionActive>(object, bits.GetI());
}
inline double ArtField::GetDouble(Object* object) {
@@ -195,12 +206,13 @@ inline double ArtField::GetDouble(Object* object) {
return bits.GetD();
}
+template<bool kTransactionActive>
inline void ArtField::SetDouble(Object* object, double d) {
DCHECK_EQ(Primitive::kPrimDouble, FieldHelper(this).GetTypeAsPrimitiveType())
<< PrettyField(this);
JValue bits;
bits.SetD(d);
- Set64(object, bits.GetJ());
+ Set64<kTransactionActive>(object, bits.GetJ());
}
inline Object* ArtField::GetObject(Object* object) {
@@ -209,10 +221,11 @@ inline Object* ArtField::GetObject(Object* object) {
return GetObj(object);
}
+template<bool kTransactionActive>
inline void ArtField::SetObject(Object* object, Object* l) {
DCHECK_EQ(Primitive::kPrimNot, FieldHelper(this).GetTypeAsPrimitiveType())
<< PrettyField(this);
- SetObj(object, l);
+ SetObj<kTransactionActive>(object, l);
}
} // namespace mirror
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index 29aade9..dd628ea 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -42,14 +42,15 @@ void ArtField::ResetClass() {
void ArtField::SetOffset(MemberOffset num_bytes) {
DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
-#if 0 // TODO enable later in boot and under !NDEBUG
- FieldHelper fh(this);
- Primitive::Type type = fh.GetTypeAsPrimitiveType();
- if (type == Primitive::kPrimDouble || type == Primitive::kPrimLong) {
- DCHECK_ALIGNED(num_bytes.Uint32Value(), 8);
+ if (kIsDebugBuild && Runtime::Current()->IsCompiler() &&
+ !Runtime::Current()->UseCompileTimeClassPath()) {
+ Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+ if (type == Primitive::kPrimDouble || type == Primitive::kPrimLong) {
+ DCHECK_ALIGNED(num_bytes.Uint32Value(), 8);
+ }
}
-#endif
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, offset_), num_bytes.Uint32Value(), false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, offset_), num_bytes.Uint32Value(), false);
}
void ArtField::VisitRoots(RootCallback* callback, void* arg) {
diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h
index 716b736..46287c3 100644
--- a/runtime/mirror/art_field.h
+++ b/runtime/mirror/art_field.h
@@ -38,7 +38,8 @@ class MANAGED ArtField : public Object {
uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, access_flags_), new_access_flags, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, access_flags_), new_access_flags, false);
}
bool IsPublic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -58,7 +59,8 @@ class MANAGED ArtField : public Object {
}
void SetDexFieldIndex(uint32_t new_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, field_dex_idx_), new_idx, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, field_dex_idx_), new_idx, false);
}
// Offset to field within an Object.
@@ -74,30 +76,42 @@ class MANAGED ArtField : public Object {
// field access, null object for static fields
bool GetBoolean(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetBoolean(Object* object, bool z) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
int8_t GetByte(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetByte(Object* object, int8_t b) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint16_t GetChar(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetChar(Object* object, uint16_t c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
int16_t GetShort(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetShort(Object* object, int16_t s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
int32_t GetInt(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetInt(Object* object, int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
int64_t GetLong(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetLong(Object* object, int64_t j) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
float GetFloat(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetFloat(Object* object, float f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
double GetDouble(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetDouble(Object* object, double d) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Object* GetObject(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetObject(Object* object, Object* l) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Raw field accesses.
uint32_t Get32(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void Set32(Object* object, uint32_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint64_t Get64(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void Set64(Object* object, uint64_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Object* GetObj(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetObj(Object* object, Object* new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Class* GetJavaLangReflectArtField() {
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 8ef3be8..d347724 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -36,7 +36,8 @@ inline Class* ArtMethod::GetDeclaringClass() {
}
inline void ArtMethod::SetDeclaringClass(Class *new_declaring_class) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, declaring_class_), new_declaring_class, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, declaring_class_),
+ new_declaring_class, false);
}
inline uint32_t ArtMethod::GetAccessFlags() {
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 67e6c7d..b5c87ad 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -73,18 +73,18 @@ void ArtMethod::ResetClass() {
}
void ArtMethod::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_),
- new_dex_cache_strings, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_),
+ new_dex_cache_strings, false);
}
void ArtMethod::SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_),
- new_dex_cache_methods, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_),
+ new_dex_cache_methods, false);
}
void ArtMethod::SetDexCacheResolvedTypes(ObjectArray<Class>* new_dex_cache_classes) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_),
- new_dex_cache_classes, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_),
+ new_dex_cache_classes, false);
}
size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) {
@@ -337,8 +337,8 @@ void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_
#else
SetNativeMethod(reinterpret_cast<void*>(art_work_around_app_jni_bugs));
#endif
- SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_),
- reinterpret_cast<const uint8_t*>(native_method), false);
+ SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_),
+ reinterpret_cast<const uint8_t*>(native_method), false);
}
}
@@ -349,8 +349,8 @@ void ArtMethod::UnregisterNative(Thread* self) {
}
void ArtMethod::SetNativeMethod(const void* native_method) {
- SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_),
- native_method, false);
+ SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_),
+ native_method, false);
}
} // namespace mirror
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index e678503..71cc7af 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -56,7 +56,8 @@ class MANAGED ArtMethod : public Object {
uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, access_flags_), new_access_flags, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, access_flags_), new_access_flags, false);
}
// Approximate what kind of method call would be used for this method.
@@ -156,7 +157,8 @@ class MANAGED ArtMethod : public Object {
}
void SetMethodIndex(uint16_t new_method_index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_), new_method_index, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_), new_method_index, false);
}
static MemberOffset MethodIndexOffset() {
@@ -168,7 +170,8 @@ class MANAGED ArtMethod : public Object {
}
void SetCodeItemOffset(uint32_t new_code_off) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_code_item_offset_), new_code_off, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_code_item_offset_), new_code_off, false);
}
// Number of 32bit registers that would be required to hold all the arguments
@@ -177,7 +180,8 @@ class MANAGED ArtMethod : public Object {
uint32_t GetDexMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetDexMethodIndex(uint32_t new_idx) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_), new_idx, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_), new_idx, false);
}
ObjectArray<String>* GetDexCacheStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -216,9 +220,8 @@ class MANAGED ArtMethod : public Object {
}
void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter) {
- SetFieldPtr<EntryPointFromInterpreter*>(
- OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
- entry_point_from_interpreter, false);
+ SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
+ entry_point_from_interpreter, false);
}
static MemberOffset EntryPointFromPortableCompiledCodeOffset() {
@@ -230,8 +233,8 @@ class MANAGED ArtMethod : public Object {
}
void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code) {
- SetFieldPtr<const void*>(EntryPointFromPortableCompiledCodeOffset(),
- entry_point_from_portable_compiled_code, false);
+ SetFieldPtr<false>(EntryPointFromPortableCompiledCodeOffset(),
+ entry_point_from_portable_compiled_code, false);
}
static MemberOffset EntryPointFromQuickCompiledCodeOffset() {
@@ -243,8 +246,8 @@ class MANAGED ArtMethod : public Object {
}
void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code) {
- SetFieldPtr<const void*>(EntryPointFromQuickCompiledCodeOffset(),
- entry_point_from_quick_compiled_code, false);
+ SetFieldPtr<false>(EntryPointFromQuickCompiledCodeOffset(),
+ entry_point_from_quick_compiled_code, false);
}
@@ -277,8 +280,8 @@ class MANAGED ArtMethod : public Object {
}
void SetMappingTable(const uint8_t* mapping_table) {
- SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_),
- mapping_table, false);
+ SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_),
+ mapping_table, false);
}
uint32_t GetOatMappingTableOffset();
@@ -292,8 +295,7 @@ class MANAGED ArtMethod : public Object {
}
void SetVmapTable(const uint8_t* vmap_table) {
- SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table,
- false);
+ SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, false);
}
uint32_t GetOatVmapTableOffset();
@@ -304,7 +306,7 @@ class MANAGED ArtMethod : public Object {
return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), false);
}
void SetNativeGcMap(const uint8_t* data) {
- SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data, false);
+ SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data, false);
}
// When building the oat need a convenient place to stuff the offset of the native GC map.
@@ -319,8 +321,9 @@ class MANAGED ArtMethod : public Object {
}
void SetFrameSizeInBytes(size_t new_frame_size_in_bytes) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_),
- new_frame_size_in_bytes, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_),
+ new_frame_size_in_bytes, false);
}
size_t GetReturnPcOffsetInBytes() {
@@ -358,8 +361,9 @@ class MANAGED ArtMethod : public Object {
}
void SetCoreSpillMask(uint32_t core_spill_mask) {
- // Computed during compilation
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_core_spill_mask_), core_spill_mask, false);
+ // Computed during compilation.
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_core_spill_mask_), core_spill_mask, false);
}
uint32_t GetFpSpillMask() {
@@ -367,8 +371,9 @@ class MANAGED ArtMethod : public Object {
}
void SetFpSpillMask(uint32_t fp_spill_mask) {
- // Computed during compilation
- SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_fp_spill_mask_), fp_spill_mask, false);
+ // Computed during compilation.
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_fp_spill_mask_), fp_spill_mask, false);
}
// Is this a CalleSaveMethod or ResolutionMethod and therefore doesn't adhere to normal
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index a5f743b..4c2bdb0 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -64,8 +64,8 @@ inline void Class::SetDirectMethods(ObjectArray<ArtMethod>* new_direct_methods)
DCHECK(NULL == GetFieldObject<ObjectArray<ArtMethod> >(
OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_), false));
DCHECK_NE(0, new_direct_methods->GetLength());
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_),
- new_direct_methods, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_),
+ new_direct_methods, false);
}
inline ArtMethod* Class::GetDirectMethod(int32_t i) {
@@ -77,7 +77,7 @@ inline void Class::SetDirectMethod(uint32_t i, ArtMethod* f) // TODO: uint16_t
ObjectArray<ArtMethod>* direct_methods =
GetFieldObject<ObjectArray<ArtMethod> >(
OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_), false);
- direct_methods->Set(i, f);
+ direct_methods->Set<false>(i, f);
}
// Returns the number of static, private, and constructor methods.
@@ -93,10 +93,10 @@ inline ObjectArray<ArtMethod>* Class::GetVirtualMethods() {
inline void Class::SetVirtualMethods(ObjectArray<ArtMethod>* new_virtual_methods) {
// TODO: we reassign virtual methods to grow the table for miranda
- // methods.. they should really just be assigned once
+ // methods.. they should really just be assigned once.
DCHECK_NE(0, new_virtual_methods->GetLength());
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_),
- new_virtual_methods, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_),
+ new_virtual_methods, false);
}
inline uint32_t Class::NumVirtualMethods() {
@@ -118,7 +118,7 @@ inline void Class::SetVirtualMethod(uint32_t i, ArtMethod* f) // TODO: uint16_t
ObjectArray<ArtMethod>* virtual_methods =
GetFieldObject<ObjectArray<ArtMethod> >(
OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_), false);
- virtual_methods->Set(i, f);
+ virtual_methods->Set<false>(i, f);
}
inline ObjectArray<ArtMethod>* Class::GetVTable() {
@@ -132,7 +132,7 @@ inline ObjectArray<ArtMethod>* Class::GetVTableDuringLinking() {
}
inline void Class::SetVTable(ObjectArray<ArtMethod>* new_vtable) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable, false);
}
inline ObjectArray<ArtMethod>* Class::GetImTable() {
@@ -140,7 +140,7 @@ inline ObjectArray<ArtMethod>* Class::GetImTable() {
}
inline void Class::SetImTable(ObjectArray<ArtMethod>* new_imtable) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable, false);
}
inline bool Class::Implements(Class* klass) {
@@ -347,7 +347,7 @@ inline int32_t Class::GetIfTableCount() {
}
inline void Class::SetIfTable(IfTable* new_iftable) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable, false);
}
inline ObjectArray<ArtField>* Class::GetIFields() {
@@ -359,7 +359,7 @@ inline void Class::SetIFields(ObjectArray<ArtField>* new_ifields)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(NULL == GetFieldObject<ObjectArray<ArtField> >(
OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false));
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), new_ifields, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), new_ifields, false);
}
inline ObjectArray<ArtField>* Class::GetSFields() {
@@ -371,7 +371,7 @@ inline void Class::SetSFields(ObjectArray<ArtField>* new_sfields)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(NULL == GetFieldObject<ObjectArray<ArtField> >(
OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false));
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields, false);
}
inline uint32_t Class::NumStaticFields() {
@@ -387,7 +387,7 @@ inline void Class::SetStaticField(uint32_t i, ArtField* f) // TODO: uint16_t
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ObjectArray<ArtField>* sfields= GetFieldObject<ObjectArray<ArtField> >(
OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false);
- sfields->Set(i, f);
+ sfields->Set<false>(i, f);
}
inline uint32_t Class::NumInstanceFields() {
@@ -403,12 +403,16 @@ inline void Class::SetInstanceField(uint32_t i, ArtField* f) // TODO: uint16_t
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ObjectArray<ArtField>* ifields= GetFieldObject<ObjectArray<ArtField> >(
OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false);
- ifields->Set(i, f);
+ ifields->Set<false>(i, f);
}
inline void Class::SetVerifyErrorClass(Class* klass) {
CHECK(klass != NULL) << PrettyClass(this);
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
+ } else {
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
+ }
}
inline uint32_t Class::GetAccessFlags() {
@@ -425,7 +429,11 @@ inline String* Class::GetName() {
return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(Class, name_), false);
}
inline void Class::SetName(String* name) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false);
+ } else {
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false);
+ }
}
inline void Class::CheckObjectAlloc() {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 99a35e3..6446d02 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -113,7 +113,11 @@ void Class::SetStatus(Status new_status, Thread* self) {
self->SetException(gc_safe_throw_location, old_exception.get());
}
CHECK(sizeof(Status) == sizeof(uint32_t)) << PrettyClass(this);
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetField32<true>(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
+ } else {
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
+ }
// Classes that are being resolved or initialized need to notify waiters that the class status
// changed. See ClassLinker::EnsureResolved and ClassLinker::WaitForInitializeClass.
if ((old_status >= kStatusResolved || new_status >= kStatusResolved) &&
@@ -123,7 +127,7 @@ void Class::SetStatus(Status new_status, Thread* self) {
}
void Class::SetDexCache(DexCache* new_dex_cache) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache, false);
}
void Class::SetClassSize(uint32_t new_class_size) {
@@ -131,7 +135,8 @@ void Class::SetClassSize(uint32_t new_class_size) {
DumpClass(LOG(ERROR), kDumpClassFullDetail);
CHECK_GE(new_class_size, GetClassSize()) << " class=" << PrettyTypeOf(this);
}
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size, false);
}
// Return the class' name. The exact format is bizarre, but it's the specified behavior for
@@ -254,8 +259,9 @@ void Class::SetReferenceInstanceOffsets(uint32_t new_reference_offsets) {
}
CHECK_EQ((size_t)__builtin_popcount(new_reference_offsets), count);
}
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_),
- new_reference_offsets, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_),
+ new_reference_offsets, false);
}
void Class::SetReferenceStaticOffsets(uint32_t new_reference_offsets) {
@@ -265,8 +271,9 @@ void Class::SetReferenceStaticOffsets(uint32_t new_reference_offsets) {
CHECK_EQ((size_t)__builtin_popcount(new_reference_offsets),
NumReferenceStaticFieldsDuringLinking());
}
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_),
- new_reference_offsets, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_),
+ new_reference_offsets, false);
}
bool Class::IsInSamePackage(const StringPiece& descriptor1, const StringPiece& descriptor2) {
@@ -332,7 +339,11 @@ bool Class::IsArtMethodClass() {
}
void Class::SetClassLoader(ClassLoader* new_class_loader) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
+ } else {
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
+ }
}
ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const Signature& signature) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 82c8264..cd8504b 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -174,7 +174,8 @@ class MANAGED Class : public Object {
uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags, false);
}
// Returns true if the class is an interface.
@@ -275,7 +276,7 @@ class MANAGED Class : public Object {
void SetPrimitiveType(Primitive::Type new_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type, false);
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type, false);
}
// Returns true if the class is a primitive type.
@@ -357,7 +358,11 @@ class MANAGED Class : public Object {
void SetComponentType(Class* new_component_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(GetComponentType() == NULL);
DCHECK(new_component_type != NULL);
- SetFieldObject(ComponentTypeOffset(), new_component_type, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(ComponentTypeOffset(), new_component_type, false);
+ } else {
+ SetFieldObject<false>(ComponentTypeOffset(), new_component_type, false);
+ }
}
size_t GetComponentSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -406,7 +411,8 @@ class MANAGED Class : public Object {
void SetObjectSize(uint32_t new_object_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(!IsVariableSize());
- return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size, false);
+ // Not called within a transaction.
+ return SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size, false);
}
// Returns true if this class is in the same packages as that class.
@@ -499,7 +505,7 @@ class MANAGED Class : public Object {
false);
DCHECK(old_super_class == nullptr || old_super_class == new_super_class);
DCHECK(new_super_class != nullptr);
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, super_class_), new_super_class, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, super_class_), new_super_class, false);
}
bool HasSuperClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -672,7 +678,9 @@ class MANAGED Class : public Object {
}
void SetNumReferenceInstanceFields(uint32_t new_num) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), new_num, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), new_num,
+ false);
}
uint32_t GetReferenceInstanceOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -700,7 +708,8 @@ class MANAGED Class : public Object {
}
void SetNumReferenceStaticFields(uint32_t new_num) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), new_num, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), new_num, false);
}
// Gets the static fields of the class.
@@ -763,7 +772,13 @@ class MANAGED Class : public Object {
}
void SetClinitThreadId(pid_t new_clinit_thread_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetField32<true>(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id,
+ false);
+ } else {
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id,
+ false);
+ }
}
Class* GetVerifyErrorClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -776,7 +791,8 @@ class MANAGED Class : public Object {
}
void SetDexClassDefIndex(uint16_t class_def_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx, false);
}
uint16_t GetDexTypeIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -784,7 +800,8 @@ class MANAGED Class : public Object {
}
void SetDexTypeIndex(uint16_t type_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx, false);
+ // Not called within a transaction.
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx, false);
}
static Class* GetJavaLangClass() {
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index fa0900c..0a77db3 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -44,12 +44,12 @@ void DexCache::Init(const DexFile* dex_file,
CHECK(resolved_methods != nullptr);
CHECK(resolved_fields != nullptr);
- SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false);
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location, false);
- SetFieldObject(StringsOffset(), strings, false);
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types, false);
- SetFieldObject(ResolvedMethodsOffset(), resolved_methods, false);
- SetFieldObject(ResolvedFieldsOffset(), resolved_fields, false);
+ SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location, false);
+ SetFieldObject<false>(StringsOffset(), strings, false);
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types, false);
+ SetFieldObject<false>(ResolvedMethodsOffset(), resolved_methods, false);
+ SetFieldObject<false>(ResolvedFieldsOffset(), resolved_fields, false);
Runtime* runtime = Runtime::Current();
if (runtime->HasResolutionMethod()) {
@@ -57,7 +57,7 @@ void DexCache::Init(const DexFile* dex_file,
ArtMethod* trampoline = runtime->GetResolutionMethod();
size_t length = resolved_methods->GetLength();
for (size_t i = 0; i < length; i++) {
- resolved_methods->SetWithoutChecks(i, trampoline);
+ resolved_methods->SetWithoutChecks<false>(i, trampoline);
}
}
}
@@ -69,7 +69,7 @@ void DexCache::Fixup(ArtMethod* trampoline) {
size_t length = resolved_methods->GetLength();
for (size_t i = 0; i < length; i++) {
if (resolved_methods->GetWithoutChecks(i) == nullptr) {
- resolved_methods->SetWithoutChecks(i, trampoline);
+ resolved_methods->SetWithoutChecks<false>(i, trampoline);
}
}
}
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 99529f0..843f860 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -90,6 +90,7 @@ class MANAGED DexCache : public Object {
void SetResolvedString(uint32_t string_idx, String* resolved)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // TODO default transaction support.
GetStrings()->Set(string_idx, resolved);
}
@@ -99,6 +100,7 @@ class MANAGED DexCache : public Object {
void SetResolvedType(uint32_t type_idx, Class* resolved)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // TODO default transaction support.
GetResolvedTypes()->Set(type_idx, resolved);
}
@@ -140,7 +142,7 @@ class MANAGED DexCache : public Object {
}
void SetDexFile(const DexFile* dex_file) {
- return SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false);
+ return SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false);
}
private:
diff --git a/runtime/mirror/iftable-inl.h b/runtime/mirror/iftable-inl.h
index 9d5fa74..ec3e514 100644
--- a/runtime/mirror/iftable-inl.h
+++ b/runtime/mirror/iftable-inl.h
@@ -26,7 +26,7 @@ inline void IfTable::SetInterface(int32_t i, Class* interface) {
DCHECK(interface != NULL);
DCHECK(interface->IsInterface());
DCHECK(Get((i * kMax) + kInterface) == NULL);
- Set((i * kMax) + kInterface, interface);
+ Set<false>((i * kMax) + kInterface, interface);
}
} // namespace mirror
diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h
index be83d03..bb4cd41 100644
--- a/runtime/mirror/iftable.h
+++ b/runtime/mirror/iftable.h
@@ -52,7 +52,7 @@ class MANAGED IfTable : public ObjectArray<Object> {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(new_ma != NULL);
DCHECK(Get((i * kMax) + kMethodArray) == NULL);
- Set((i * kMax) + kMethodArray, new_ma);
+ Set<false>((i * kMax) + kMethodArray, new_ma);
}
size_t Count() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2392561..70291c1 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -40,7 +40,10 @@ inline void Object::SetClass(Class* new_klass) {
// new_klass may be NULL prior to class linker initialization.
// We don't mark the card as this occurs as part of object allocation. Not all objects have
// backing cards, such as large objects.
- SetFieldObjectWithoutWriteBarrier(OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false, false);
+ // We use non transactional version since we can't undo this write. We also disable checking as
+ // we may run in transaction mode here.
+ SetFieldObjectWithoutWriteBarrier<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, klass_),
+ new_klass, false, false);
}
inline LockWord Object::GetLockWord() {
@@ -48,12 +51,14 @@ inline LockWord Object::GetLockWord() {
}
inline void Object::SetLockWord(LockWord new_val) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true);
+ // Force use of non-transactional mode and do not check.
+ SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true);
}
inline bool Object::CasLockWord(LockWord old_val, LockWord new_val) {
- return CasField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(),
- new_val.GetValue());
+ // Force use of non-transactional mode and do not check.
+ return CasField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(),
+ new_val.GetValue());
}
inline uint32_t Object::GetLockOwnerThreadId() {
@@ -199,6 +204,18 @@ inline LongArray* Object::AsLongArray() {
return down_cast<LongArray*>(this);
}
+inline FloatArray* Object::AsFloatArray() {
+ DCHECK(GetClass()->IsArrayClass());
+ DCHECK(GetClass()->GetComponentType()->IsPrimitiveFloat());
+ return down_cast<FloatArray*>(this);
+}
+
+inline DoubleArray* Object::AsDoubleArray() {
+ DCHECK(GetClass()->IsArrayClass());
+ DCHECK(GetClass()->GetComponentType()->IsPrimitiveDouble());
+ return down_cast<DoubleArray*>(this);
+}
+
inline String* Object::AsString() {
DCHECK(GetClass()->IsStringClass());
return down_cast<String*>(this);
@@ -253,8 +270,16 @@ inline int32_t Object::GetField32(MemberOffset field_offset, bool is_volatile) {
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile,
bool this_is_valid) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField32(this, field_offset, GetField32(field_offset, is_volatile),
+ is_volatile);
+ }
if (this_is_valid) {
VerifyObject(this);
}
@@ -269,7 +294,14 @@ inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, boo
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline bool Object::CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true);
+ }
VerifyObject(this);
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);
@@ -289,8 +321,16 @@ inline int64_t Object::GetField64(MemberOffset field_offset, bool is_volatile) {
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile,
bool this_is_valid) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField64(this, field_offset, GetField64(field_offset, is_volatile),
+ is_volatile);
+ }
if (this_is_valid) {
VerifyObject(this);
}
@@ -309,7 +349,14 @@ inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, boo
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline bool Object::CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true);
+ }
VerifyObject(this);
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
volatile int64_t* addr = reinterpret_cast<volatile int64_t*>(raw_addr);
@@ -331,8 +378,17 @@ inline T* Object::GetFieldObject(MemberOffset field_offset, bool is_volatile) {
return result;
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value,
bool is_volatile, bool this_is_valid) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldReference(this, field_offset,
+ GetFieldObject<Object>(field_offset, is_volatile),
+ true);
+ }
if (this_is_valid) {
VerifyObject(this);
}
@@ -349,16 +405,26 @@ inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset,
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline void Object::SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile,
bool this_is_valid) {
- SetFieldObjectWithoutWriteBarrier(field_offset, new_value, is_volatile, this_is_valid);
+ SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(field_offset, new_value,
+ is_volatile,
+ this_is_valid);
if (new_value != nullptr) {
CheckFieldAssignment(field_offset, new_value);
Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline bool Object::CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
+ }
VerifyObject(this);
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 5874976..eb118c7 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -23,6 +23,7 @@
#include "cutils/atomic-inline.h"
#include "object_reference.h"
#include "offsets.h"
+#include "runtime.h"
namespace art {
@@ -130,6 +131,9 @@ class MANAGED Object {
IntArray* AsIntArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
LongArray* AsLongArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ FloatArray* AsFloatArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ DoubleArray* AsDoubleArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
String* AsString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Throwable* AsThrowable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -155,12 +159,15 @@ class MANAGED Object {
// Accessor for Java type fields.
template<class T> T* GetFieldObject(MemberOffset field_offset, bool is_volatile)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true>
void SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value,
bool is_volatile, bool this_is_valid = true)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true>
void SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile,
bool this_is_valid = true)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true>
bool CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -172,27 +179,35 @@ class MANAGED Object {
int32_t GetField32(MemberOffset field_offset, bool is_volatile);
+ template<bool kTransactionActive, bool kCheckTransaction = true>
void SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile,
bool this_is_valid = true);
+ template<bool kTransactionActive, bool kCheckTransaction = true>
bool CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
int64_t GetField64(MemberOffset field_offset, bool is_volatile);
+ template<bool kTransactionActive, bool kCheckTransaction = true>
void SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile,
bool this_is_valid = true);
+ template<bool kTransactionActive, bool kCheckTransaction = true>
bool CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<typename T>
+ template<bool kTransactionActive, bool kCheckTransaction = true, typename T>
void SetFieldPtr(MemberOffset field_offset, T new_value, bool is_volatile,
bool this_is_valid = true) {
#ifndef __LP64__
- SetField32(field_offset, reinterpret_cast<int32_t>(new_value), is_volatile, this_is_valid);
+ SetField32<kTransactionActive, kCheckTransaction>(field_offset,
+ reinterpret_cast<int32_t>(new_value),
+ is_volatile, this_is_valid);
#else
- SetField64(field_offset, reinterpret_cast<int64_t>(new_value), is_volatile, this_is_valid);
+ SetField64<kTransactionActive, kCheckTransaction>(field_offset,
+ reinterpret_cast<int64_t>(new_value),
+ is_volatile, this_is_valid);
#endif
}
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index c342479..521b6ce 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -72,26 +72,39 @@ inline bool ObjectArray<T>::CheckAssignable(T* object) {
template<class T>
inline void ObjectArray<T>::Set(int32_t i, T* object) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ Set<true>(i, object);
+ } else {
+ Set<false>(i, object);
+ }
+}
+
+template<class T>
+template<bool kTransactionActive, bool kCheckTransaction>
+inline void ObjectArray<T>::Set(int32_t i, T* object) {
if (LIKELY(CheckIsValidIndex(i) && CheckAssignable(object))) {
- SetFieldObject(OffsetOfElement(i), object, false);
+ SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false);
} else {
DCHECK(Thread::Current()->IsExceptionPending());
}
}
template<class T>
+template<bool kTransactionActive, bool kCheckTransaction>
inline void ObjectArray<T>::SetWithoutChecks(int32_t i, T* object) {
DCHECK(CheckIsValidIndex(i));
DCHECK(CheckAssignable(object));
- SetFieldObject(OffsetOfElement(i), object, false);
+ SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false);
}
template<class T>
+template<bool kTransactionActive, bool kCheckTransaction>
inline void ObjectArray<T>::SetWithoutChecksAndWriteBarrier(int32_t i, T* object) {
DCHECK(CheckIsValidIndex(i));
// TODO: enable this check. It fails when writing the image in ImageWriter::FixupObjectArray.
// DCHECK(CheckAssignable(object));
- SetFieldObjectWithoutWriteBarrier(OffsetOfElement(i), object, false);
+ SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(OffsetOfElement(i),
+ object, false);
}
template<class T>
@@ -164,15 +177,15 @@ inline void ObjectArray<T>::AssignableCheckingMemcpy(int32_t dst_pos, ObjectArra
o = src->GetWithoutChecks(src_pos + i);
if (o == nullptr) {
// Null is always assignable.
- SetWithoutChecks(dst_pos + i, nullptr);
+ SetWithoutChecks<false>(dst_pos + i, nullptr);
} else {
// TODO: use the underlying class reference to avoid uncompression when not necessary.
Class* o_class = o->GetClass();
if (LIKELY(lastAssignableElementClass == o_class)) {
- SetWithoutChecks(dst_pos + i, o);
+ SetWithoutChecks<false>(dst_pos + i, o);
} else if (LIKELY(dst_class->IsAssignableFrom(o_class))) {
lastAssignableElementClass = o_class;
- SetWithoutChecks(dst_pos + i, o);
+ SetWithoutChecks<false>(dst_pos + i, o);
} else {
// Can't put this element into the array, break to perform write-barrier and throw
// exception.
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index 347494e..668b276 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -40,12 +40,20 @@ class MANAGED ObjectArray : public Array {
bool CheckAssignable(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void Set(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // TODO fix thread safety analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+ template<bool kTransactionActive, bool kCheckTransaction = true>
+ void Set(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
// Set element without bound and element type checks, to be used in limited
- // circumstances, such as during boot image writing
- void SetWithoutChecks(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetWithoutChecksAndWriteBarrier(int32_t i, T* object)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // circumstances, such as during boot image writing.
+ // TODO fix thread safety analysis broken by the use of template. This should be
+ // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+ template<bool kTransactionActive, bool kCheckTransaction = true>
+ void SetWithoutChecks(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
+ // TODO fix thread safety analysis broken by the use of template. This should be
+ // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+ template<bool kTransactionActive, bool kCheckTransaction = true>
+ void SetWithoutChecksAndWriteBarrier(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
T* GetWithoutChecks(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index db9723b..40c3748 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -116,10 +116,10 @@ TEST_F(ObjectTest, AllocObjectArray) {
EXPECT_EQ(2, oa->GetLength());
EXPECT_TRUE(oa->Get(0) == NULL);
EXPECT_TRUE(oa->Get(1) == NULL);
- oa->Set(0, oa.get());
+ oa->Set<false>(0, oa.get());
EXPECT_TRUE(oa->Get(0) == oa.get());
EXPECT_TRUE(oa->Get(1) == NULL);
- oa->Set(1, oa.get());
+ oa->Set<false>(1, oa.get());
EXPECT_TRUE(oa->Get(0) == oa.get());
EXPECT_TRUE(oa->Get(1) == oa.get());
@@ -235,12 +235,12 @@ TEST_F(ObjectTest, CreateMultiArray) {
SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass("I"));
SirtRef<IntArray> dims(soa.Self(), IntArray::Alloc(soa.Self(), 1));
- dims->Set(0, 1);
+ dims->Set<false>(0, 1);
Array* multi = Array::CreateMultiArray(soa.Self(), c, dims);
EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[I"));
EXPECT_EQ(1, multi->GetLength());
- dims->Set(0, -1);
+ dims->Set<false>(0, -1);
multi = Array::CreateMultiArray(soa.Self(), c, dims);
EXPECT_TRUE(soa.Self()->IsExceptionPending());
EXPECT_EQ(PrettyDescriptor(soa.Self()->GetException(NULL)->GetClass()),
@@ -250,8 +250,8 @@ TEST_F(ObjectTest, CreateMultiArray) {
dims.reset(IntArray::Alloc(soa.Self(), 2));
for (int i = 1; i < 20; ++i) {
for (int j = 0; j < 20; ++j) {
- dims->Set(0, i);
- dims->Set(1, j);
+ dims->Set<false>(0, i);
+ dims->Set<false>(1, j);
multi = Array::CreateMultiArray(soa.Self(), c, dims);
EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[[I"));
EXPECT_EQ(i, multi->GetLength());
@@ -301,10 +301,10 @@ TEST_F(ObjectTest, StaticFieldFromCode) {
EXPECT_TRUE(s0 != NULL);
SirtRef<CharArray> char_array(soa.Self(), CharArray::Alloc(soa.Self(), 0));
- field->SetObj(field->GetDeclaringClass(), char_array.get());
+ field->SetObj<false>(field->GetDeclaringClass(), char_array.get());
EXPECT_EQ(char_array.get(), field->GetObj(klass));
- field->SetObj(field->GetDeclaringClass(), NULL);
+ field->SetObj<false>(field->GetDeclaringClass(), NULL);
EXPECT_EQ(NULL, field->GetObj(klass));
// TODO: more exhaustive tests of all 6 cases of ArtField::*FromCode
@@ -387,8 +387,8 @@ TEST_F(ObjectTest, StringLength) {
EXPECT_EQ(string->GetLength(), 7);
EXPECT_EQ(string->GetUtfLength(), 7);
- string->SetOffset(2);
- string->SetCount(5);
+ string->SetOffset<false>(2);
+ string->SetCount<false>(5);
EXPECT_TRUE(string->Equals("droid"));
EXPECT_EQ(string->GetLength(), 5);
EXPECT_EQ(string->GetUtfLength(), 5);
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index 2e33198..6bc695d 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -46,18 +46,28 @@ StackTraceElement* StackTraceElement::Alloc(Thread* self,
StackTraceElement* trace =
down_cast<StackTraceElement*>(GetStackTraceElement()->AllocObject(self));
if (LIKELY(trace != NULL)) {
- trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_),
- declaring_class.get(), false);
- trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, method_name_),
- method_name.get(), false);
- trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, file_name_),
- file_name.get(), false);
- trace->SetField32(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, line_number_),
- line_number, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ trace->Init<true>(declaring_class, method_name, file_name, line_number);
+ } else {
+ trace->Init<false>(declaring_class, method_name, file_name, line_number);
+ }
}
return trace;
}
+template<bool kTransactionActive>
+void StackTraceElement::Init(SirtRef<String>& declaring_class, SirtRef<String>& method_name,
+ SirtRef<String>& file_name, int32_t line_number) {
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_),
+ declaring_class.get(), false);
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, method_name_),
+ method_name.get(), false);
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, file_name_),
+ file_name.get(), false);
+ SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, line_number_),
+ line_number, false);
+}
+
void StackTraceElement::VisitRoots(RootCallback* callback, void* arg) {
if (java_lang_StackTraceElement_ != nullptr) {
java_lang_StackTraceElement_ = down_cast<Class*>(
diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h
index 51817f6..779ec4b 100644
--- a/runtime/mirror/stack_trace_element.h
+++ b/runtime/mirror/stack_trace_element.h
@@ -67,6 +67,11 @@ class MANAGED StackTraceElement : public Object {
HeapReference<String> method_name_;
int32_t line_number_;
+ template<bool kTransactionActive>
+ void Init(SirtRef<String>& declaring_class, SirtRef<String>& method_name,
+ SirtRef<String>& file_name, int32_t line_number)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static Class* GetStackTraceElement() {
DCHECK(java_lang_StackTraceElement_ != NULL);
return java_lang_StackTraceElement_;
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 6f4ead9..cd63c39 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -33,7 +33,7 @@ CharArray* String::GetCharArray() {
return GetFieldObject<CharArray>(ValueOffset(), false);
}
-void String::ComputeHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void String::ComputeHashCode() {
SetHashCode(ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength()));
}
@@ -59,9 +59,10 @@ int32_t String::FastIndexOf(int32_t ch, int32_t start) {
return -1;
}
-void String::SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+template<bool kTransactionActive>
+void String::SetArray(CharArray* new_array) {
DCHECK(new_array != NULL);
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(String, array_), new_array, false);
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(String, array_), new_array, false);
}
// TODO: get global references for these
@@ -169,8 +170,13 @@ String* String::Alloc(Thread* self, const SirtRef<CharArray>& array) {
// Hold reference in case AllocObject causes GC.
String* string = down_cast<String*>(GetJavaLangString()->AllocObject(self));
if (LIKELY(string != nullptr)) {
- string->SetArray(array.get());
- string->SetCount(array->GetLength());
+ if (Runtime::Current()->IsActiveTransaction()) {
+ string->SetArray<true>(array.get());
+ string->SetCount<true>(array->GetLength());
+ } else {
+ string->SetArray<false>(array.get());
+ string->SetCount<false>(array->GetLength());
+ }
}
return string;
}
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 57ec314..1340e7d 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -112,19 +112,23 @@ class MANAGED String : public Object {
private:
void SetHashCode(int32_t new_hash_code) {
+ // Hash code is invariant so use non-transactional mode. Also disable check as we may run inside
+ // a transaction.
DCHECK_EQ(0, GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), false));
- SetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code, false);
+ SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code, false);
}
+ template<bool kTransactionActive>
void SetCount(int32_t new_count) {
DCHECK_LE(0, new_count);
- SetField32(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count, false);
+ SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count, false);
}
+ template<bool kTransactionActive>
void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_LE(0, new_offset);
DCHECK_GE(GetLength(), new_offset);
- SetField32(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset, false);
+ SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset, false);
}
static String* Alloc(Thread* self, int32_t utf16_length)
@@ -133,6 +137,7 @@ class MANAGED String : public Object {
static String* Alloc(Thread* self, const SirtRef<CharArray>& array)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive>
void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index a57bd43..fef7d36 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -38,7 +38,11 @@ void Throwable::SetCause(Throwable* cause) {
Throwable* current_cause = GetFieldObject<Throwable>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_),
false);
CHECK(current_cause == NULL || current_cause == this);
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false);
+ } else {
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false);
+ }
}
bool Throwable::IsCheckedException() {
diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h
index de71957..c1438d7 100644
--- a/runtime/mirror/throwable.h
+++ b/runtime/mirror/throwable.h
@@ -31,7 +31,13 @@ namespace mirror {
class MANAGED Throwable : public Object {
public:
void SetDetailMessage(String* new_detail_message) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message, false);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message,
+ false);
+ } else {
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message,
+ false);
+ }
}
String* GetDetailMessage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), false);
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 269a4a3..40aebfa 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -183,32 +183,32 @@ static void SetFieldValue(ScopedFastNativeObjectAccess& soa, mirror::Object* o,
o = sirt_obj.get();
switch (FieldHelper(f).GetTypeAsPrimitiveType()) {
case Primitive::kPrimBoolean:
- f->SetBoolean(o, new_value.GetZ());
+ f->SetBoolean<false>(o, new_value.GetZ());
break;
case Primitive::kPrimByte:
- f->SetByte(o, new_value.GetB());
+ f->SetByte<false>(o, new_value.GetB());
break;
case Primitive::kPrimChar:
- f->SetChar(o, new_value.GetC());
+ f->SetChar<false>(o, new_value.GetC());
break;
case Primitive::kPrimDouble:
- f->SetDouble(o, new_value.GetD());
+ f->SetDouble<false>(o, new_value.GetD());
break;
case Primitive::kPrimFloat:
- f->SetFloat(o, new_value.GetF());
+ f->SetFloat<false>(o, new_value.GetF());
break;
case Primitive::kPrimInt:
- f->SetInt(o, new_value.GetI());
+ f->SetInt<false>(o, new_value.GetI());
break;
case Primitive::kPrimLong:
- f->SetLong(o, new_value.GetJ());
+ f->SetLong<false>(o, new_value.GetJ());
break;
case Primitive::kPrimShort:
- f->SetShort(o, new_value.GetS());
+ f->SetShort<false>(o, new_value.GetS());
break;
case Primitive::kPrimNot:
if (allow_references) {
- f->SetObject(o, new_value.GetL());
+ f->SetObject<false>(o, new_value.GetL());
break;
}
// Else fall through to report an error.
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 6727862..ad0f317 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -27,7 +27,8 @@ static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj,
jint expectedValue, jint newValue) {
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
- bool success = obj->CasField32(MemberOffset(offset), expectedValue, newValue);
+ // JNI must use non transactional mode.
+ bool success = obj->CasField32<false>(MemberOffset(offset), expectedValue, newValue);
return success ? JNI_TRUE : JNI_FALSE;
}
@@ -35,7 +36,8 @@ static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj,
jlong expectedValue, jlong newValue) {
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
- bool success = obj->CasField64(MemberOffset(offset), expectedValue, newValue);
+ // JNI must use non transactional mode.
+ bool success = obj->CasField64<false>(MemberOffset(offset), expectedValue, newValue);
return success ? JNI_TRUE : JNI_FALSE;
}
@@ -45,7 +47,8 @@ static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaOb
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
- bool success = obj->CasFieldObject(MemberOffset(offset), expectedValue, newValue);
+ // JNI must use non transactional mode.
+ bool success = obj->CasFieldObject<false>(MemberOffset(offset), expectedValue, newValue);
return success ? JNI_TRUE : JNI_FALSE;
}
@@ -64,14 +67,16 @@ static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong o
static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
- obj->SetField32(MemberOffset(offset), newValue, false);
+ // JNI must use non transactional mode.
+ obj->SetField32<false>(MemberOffset(offset), newValue, false);
}
static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
jint newValue) {
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
- obj->SetField32(MemberOffset(offset), newValue, true);
+ // JNI must use non transactional mode.
+ obj->SetField32<false>(MemberOffset(offset), newValue, true);
}
static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset,
@@ -79,7 +84,8 @@ static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong of
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
QuasiAtomic::MembarStoreStore();
- obj->SetField32(MemberOffset(offset), newValue, false);
+ // JNI must use non transactional mode.
+ obj->SetField32<false>(MemberOffset(offset), newValue, false);
}
static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
@@ -97,14 +103,16 @@ static jlong Unsafe_getLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong
static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
- obj->SetField64(MemberOffset(offset), newValue, false);
+ // JNI must use non transactional mode.
+ obj->SetField64<false>(MemberOffset(offset), newValue, false);
}
static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
jlong newValue) {
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
- obj->SetField64(MemberOffset(offset), newValue, true);
+ // JNI must use non transactional mode.
+ obj->SetField64<false>(MemberOffset(offset), newValue, true);
}
static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset,
@@ -112,7 +120,8 @@ static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong o
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
QuasiAtomic::MembarStoreStore();
- obj->SetField64(MemberOffset(offset), newValue, false);
+ // JNI must use non transactional mode.
+ obj->SetField64<false>(MemberOffset(offset), newValue, false);
}
static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
@@ -134,7 +143,8 @@ static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
- obj->SetFieldObject(MemberOffset(offset), newValue, false);
+ // JNI must use non transactional mode.
+ obj->SetFieldObject<false>(MemberOffset(offset), newValue, false);
}
static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
@@ -142,7 +152,8 @@ static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlon
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
- obj->SetFieldObject(MemberOffset(offset), newValue, true);
+ // JNI must use non transactional mode.
+ obj->SetFieldObject<false>(MemberOffset(offset), newValue, true);
}
static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset,
@@ -151,7 +162,8 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
QuasiAtomic::MembarStoreStore();
- obj->SetFieldObject(MemberOffset(offset), newValue, false);
+ // JNI must use non transactional mode.
+ obj->SetFieldObject<false>(MemberOffset(offset), newValue, false);
}
static jint Unsafe_getArrayBaseOffsetForComponentType(JNIEnv* env, jclass, jobject component_class) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 6ca45e8..e1b0ed4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -60,6 +60,7 @@
#include "thread.h"
#include "thread_list.h"
#include "trace.h"
+#include "transaction.h"
#include "profiler.h"
#include "UniquePtr.h"
#include "verifier/method_verifier.h"
@@ -120,7 +121,8 @@ Runtime::Runtime()
main_thread_group_(nullptr),
system_thread_group_(nullptr),
system_class_loader_(nullptr),
- dump_gc_performance_on_shutdown_(false) {
+ dump_gc_performance_on_shutdown_(false),
+ preinitialization_transaction(nullptr) {
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
callee_save_methods_[i] = nullptr;
}
@@ -826,7 +828,8 @@ jobject CreateSystemClassLoader() {
thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;");
CHECK(contextClassLoader != NULL);
- contextClassLoader->SetObject(soa.Self()->GetPeer(), class_loader.get());
+ // We can't run in a transaction yet.
+ contextClassLoader->SetObject<false>(soa.Self()->GetPeer(), class_loader.get());
return env->NewGlobalRef(system_class_loader.get());
}
@@ -1305,6 +1308,10 @@ void Runtime::VisitConcurrentRoots(RootCallback* callback, void* arg, bool only_
bool clean_dirty) {
intern_table_->VisitRoots(callback, arg, only_dirty, clean_dirty);
class_linker_->VisitRoots(callback, arg, only_dirty, clean_dirty);
+ // TODO: is it the right place ?
+ if (preinitialization_transaction != nullptr) {
+ preinitialization_transaction->VisitRoots(callback, arg);
+ }
}
void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) {
@@ -1371,7 +1378,7 @@ mirror::ObjectArray<mirror::ArtMethod>* Runtime::CreateDefaultImt(ClassLinker* c
SirtRef<mirror::ObjectArray<mirror::ArtMethod> > imtable(self, cl->AllocArtMethodArray(self, 64));
mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod();
for (size_t i = 0; i < static_cast<size_t>(imtable->GetLength()); i++) {
- imtable->Set(i, imt_conflict_method);
+ imtable->Set<false>(i, imt_conflict_method);
}
return imtable.get();
}
@@ -1547,4 +1554,74 @@ void Runtime::StartProfiler(const char *appDir, bool startImmediately) {
BackgroundMethodSamplingProfiler::Start(profile_period_s_, profile_duration_s_, appDir, profile_interval_us_,
profile_backoff_coefficient_, startImmediately);
}
+
+// Transaction support.
+// TODO move them to header file for inlining.
+bool Runtime::IsActiveTransaction() const {
+ return preinitialization_transaction != nullptr;
+}
+
+void Runtime::EnterTransactionMode(Transaction* transaction) {
+ DCHECK(IsCompiler());
+ DCHECK(transaction != nullptr);
+ DCHECK(!IsActiveTransaction());
+ preinitialization_transaction = transaction;
+}
+
+void Runtime::ExitTransactionMode() {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction = nullptr;
+}
+
+void Runtime::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset,
+ uint32_t value, bool is_volatile) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction->RecordWriteField32(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset,
+ uint64_t value, bool is_volatile) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction->RecordWriteField64(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
+ mirror::Object* value, bool is_volatile) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction->RecordWriteFieldReference(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction->RecordWriteArray(array, index, value);
+}
+
+void Runtime::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction->RecordStrongStringInsertion(s, hash_code);
+}
+
+void Runtime::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction->RecordWeakStringInsertion(s, hash_code);
+}
+
+void Runtime::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction->RecordStrongStringRemoval(s, hash_code);
+}
+
+void Runtime::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction->RecordWeakStringRemoval(s, hash_code);
+}
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 07f3d7d..159de2e 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -46,6 +46,7 @@ namespace gc {
namespace mirror {
class ArtMethod;
class ClassLoader;
+ class Array;
template<class T> class ObjectArray;
template<class T> class PrimitiveArray;
typedef PrimitiveArray<int8_t> ByteArray;
@@ -65,6 +66,7 @@ class MonitorPool;
class SignalCatcher;
class ThreadList;
class Trace;
+class Transaction;
class Runtime {
public:
@@ -474,6 +476,27 @@ class Runtime {
void StartProfiler(const char *appDir, bool startImmediately = false);
+ // Transaction support.
+ bool IsActiveTransaction() const;
+ void EnterTransactionMode(Transaction* transaction);
+ void ExitTransactionMode();
+ void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
+ bool is_volatile) const;
+ void RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
+ bool is_volatile) const;
+ void RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
+ mirror::Object* value, bool is_volatile) const;
+ void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) const
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) const
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) const
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) const
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+
private:
static void InitPlatformSignalHandlers();
@@ -612,6 +635,9 @@ class Runtime {
// If true, then we dump the GC cumulative timings on shutdown.
bool dump_gc_performance_on_shutdown_;
+ // Transaction used for pre-initializing classes at compilation time.
+ Transaction* preinitialization_transaction;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 3382811..5728391 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -398,14 +398,11 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group)
// non-null value. However, because we can run without code
// available (in the compiler, in tests), we manually assign the
// fields the constructor should have set.
- soa.DecodeField(WellKnownClasses::java_lang_Thread_daemon)->
- SetBoolean(opeer_, thread_is_daemon);
- soa.DecodeField(WellKnownClasses::java_lang_Thread_group)->
- SetObject(opeer_, soa.Decode<mirror::Object*>(thread_group));
- soa.DecodeField(WellKnownClasses::java_lang_Thread_name)->
- SetObject(opeer_, soa.Decode<mirror::Object*>(thread_name.get()));
- soa.DecodeField(WellKnownClasses::java_lang_Thread_priority)->
- SetInt(opeer_, thread_priority);
+ if (runtime->IsActiveTransaction()) {
+ InitPeer<true>(soa, thread_is_daemon, thread_group, thread_name.get(), thread_priority);
+ } else {
+ InitPeer<false>(soa, thread_is_daemon, thread_group, thread_name.get(), thread_priority);
+ }
peer_thread_name.reset(GetThreadName(soa));
}
// 'thread_name' may have been null, so don't trust 'peer_thread_name' to be non-null.
@@ -414,6 +411,19 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group)
}
}
+template<bool kTransactionActive>
+void Thread::InitPeer(ScopedObjectAccess& soa, jboolean thread_is_daemon, jobject thread_group,
+ jobject thread_name, jint thread_priority) {
+ soa.DecodeField(WellKnownClasses::java_lang_Thread_daemon)->
+ SetBoolean<kTransactionActive>(opeer_, thread_is_daemon);
+ soa.DecodeField(WellKnownClasses::java_lang_Thread_group)->
+ SetObject<kTransactionActive>(opeer_, soa.Decode<mirror::Object*>(thread_group));
+ soa.DecodeField(WellKnownClasses::java_lang_Thread_name)->
+ SetObject<kTransactionActive>(opeer_, soa.Decode<mirror::Object*>(thread_name));
+ soa.DecodeField(WellKnownClasses::java_lang_Thread_priority)->
+ SetInt<kTransactionActive>(opeer_, thread_priority);
+}
+
void Thread::SetThreadName(const char* name) {
name_->assign(name);
::art::SetThreadName(name);
@@ -1020,7 +1030,11 @@ void Thread::Destroy() {
RemoveFromThreadGroup(soa);
// this.nativePeer = 0;
- soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetLong(opeer_, 0);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetLong<true>(opeer_, 0);
+ } else {
+ soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetLong<false>(opeer_, 0);
+ }
Dbg::PostThreadDeath(self);
// Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone
@@ -1309,7 +1323,8 @@ class BuildInternalStackTraceVisitor : public StackVisitor {
}
// Save PC trace in last element of method trace, also places it into the
// object graph.
- method_trace->Set(depth, dex_pc_trace);
+ // We are called from native: use non-transactional mode.
+ method_trace->Set<false>(depth, dex_pc_trace);
// Set the Object*s and assert that no thread suspension is now possible.
const char* last_no_suspend_cause =
self_->StartAssertNoThreadSuspension("Building internal stack trace");
@@ -1337,8 +1352,14 @@ class BuildInternalStackTraceVisitor : public StackVisitor {
if (m->IsRuntimeMethod()) {
return true; // Ignore runtime frames (in particular callee save).
}
- method_trace_->Set(count_, m);
- dex_pc_trace_->Set(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc());
+ // TODO dedup this code.
+ if (Runtime::Current()->IsActiveTransaction()) {
+ method_trace_->Set<true>(count_, m);
+ dex_pc_trace_->Set<true>(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc());
+ } else {
+ method_trace_->Set<false>(count_, m);
+ dex_pc_trace_->Set<false>(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc());
+ }
++count_;
return true;
}
@@ -1461,7 +1482,8 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job
if (obj == nullptr) {
return nullptr;
}
- soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->Set(i, obj);
+ // We are called from native: use non-transactional mode.
+ soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->Set<false>(i, obj);
}
return result;
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 6c072ba..48912d1 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -591,6 +591,11 @@ class PACKED(4) Thread {
void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
+ template<bool kTransactionActive>
+ void InitPeer(ScopedObjectAccess& soa, jboolean thread_is_daemon, jobject thread_group,
+ jobject thread_name, jint thread_priority)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and
// Dbg::Disconnected.
ThreadState SetStateUnsafe(ThreadState new_state) {
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
new file mode 100644
index 0000000..6adcfec
--- /dev/null
+++ b/runtime/transaction.cc
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "transaction.h"
+
+#include "base/stl_util.h"
+#include "base/logging.h"
+#include "gc/accounting/card_table-inl.h"
+#include "intern_table.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+
+#include <list>
+
+namespace art {
+
+// TODO: remove (only used for debugging purpose).
+static constexpr bool kEnableTransactionStats = false;
+
+Transaction::Transaction() : log_lock_("transaction log lock", kTransactionLogLock) {
+ CHECK(Runtime::Current()->IsCompiler());
+}
+
+Transaction::~Transaction() {
+ if (kEnableTransactionStats) {
+ MutexLock mu(Thread::Current(), log_lock_);
+ size_t objects_count = object_logs_.size();
+ size_t field_values_count = 0;
+ for (auto it : object_logs_) {
+ field_values_count += it.second.Size();
+ }
+ size_t array_count = array_logs_.size();
+ size_t array_values_count = 0;
+ for (auto it : array_logs_) {
+ array_values_count += it.second.Size();
+ }
+ size_t string_count = intern_string_logs_.size();
+ LOG(INFO) << "Transaction::~Transaction"
+ << ": objects_count=" << objects_count
+ << ", field_values_count=" << field_values_count
+ << ", array_count=" << array_count
+ << ", array_values_count=" << array_values_count
+ << ", string_count=" << string_count;
+ }
+}
+
+void Transaction::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
+ bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.Log32BitsValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
+ bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.Log64BitsValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
+ mirror::Object* value, bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.LogReferenceValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) {
+ DCHECK(array != nullptr);
+ DCHECK(array->IsArrayInstance());
+ MutexLock mu(Thread::Current(), log_lock_);
+ ArrayLog& array_log = array_logs_[array];
+ array_log.LogValue(index, value);
+}
+
+void Transaction::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) {
+ DCHECK(s != nullptr);
+ InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kInsert);
+ LogInternedString(log);
+}
+
+void Transaction::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) {
+ DCHECK(s != nullptr);
+ InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kInsert);
+ LogInternedString(log);
+}
+
+void Transaction::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) {
+ InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kRemove);
+ LogInternedString(log);
+}
+
+void Transaction::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) {
+ InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kRemove);
+ LogInternedString(log);
+}
+
+void Transaction::LogInternedString(InternStringLog& log) {
+ Locks::intern_table_lock_->AssertExclusiveHeld(Thread::Current());
+ MutexLock mu(Thread::Current(), log_lock_);
+ intern_string_logs_.push_front(log);
+}
+
+void Transaction::Abort() {
+ CHECK(!Runtime::Current()->IsActiveTransaction());
+ Thread* self = Thread::Current();
+ self->AssertNoPendingException();
+ MutexLock mu1(self, *Locks::intern_table_lock_);
+ MutexLock mu2(self, log_lock_);
+ UndoObjectModifications();
+ UndoArrayModifications();
+ UndoInternStringTableModifications();
+}
+
+void Transaction::UndoObjectModifications() {
+ // TODO we may not need to restore objects allocated during this transaction. Or we could directly
+ // remove them from the heap.
+ for (auto it : object_logs_) {
+ it.second.Undo(it.first);
+ }
+ object_logs_.clear();
+}
+
+void Transaction::UndoArrayModifications() {
+ // TODO we may not need to restore array allocated during this transaction. Or we could directly
+ // remove them from the heap.
+ for (auto it : array_logs_) {
+ it.second.Undo(it.first);
+ }
+ array_logs_.clear();
+}
+
+void Transaction::UndoInternStringTableModifications() {
+ InternTable* const intern_table = Runtime::Current()->GetInternTable();
+ // We want to undo each operation from the most recent to the oldest. List has been filled so the
+ // most recent operation is at list begin so just have to iterate over it.
+ for (InternStringLog& string_log : intern_string_logs_) {
+ string_log.Undo(intern_table);
+ }
+ intern_string_logs_.clear();
+}
+
+void Transaction::VisitRoots(RootCallback* callback, void* arg) {
+ LOG(INFO) << "Transaction::VisitRoots";
+ MutexLock mu(Thread::Current(), log_lock_);
+ VisitObjectLogs(callback, arg);
+ VisitArrayLogs(callback, arg);
+ VisitStringLogs(callback, arg);
+}
+
+void Transaction::VisitObjectLogs(RootCallback* callback, void* arg) {
+ // List of moving roots.
+ typedef std::pair<mirror::Object*, mirror::Object*> ObjectPair;
+ std::list<ObjectPair> moving_roots;
+
+ // Visit roots.
+ for (auto it : object_logs_) {
+ it.second.VisitRoots(callback, arg);
+ mirror::Object* old_root = it.first;
+ mirror::Object* new_root = callback(old_root, arg, 0, kRootUnknown);
+ if (new_root != old_root) {
+ moving_roots.push_back(std::make_pair(old_root, new_root));
+ }
+ }
+
+ // Update object logs with moving roots.
+ for (const ObjectPair& pair : moving_roots) {
+ mirror::Object* old_root = pair.first;
+ mirror::Object* new_root = pair.second;
+ auto old_root_it = object_logs_.find(old_root);
+ CHECK(old_root_it != object_logs_.end());
+ CHECK(object_logs_.find(new_root) == object_logs_.end());
+ object_logs_.insert(std::make_pair(new_root, old_root_it->second));
+ object_logs_.erase(old_root_it);
+ }
+}
+
+void Transaction::VisitArrayLogs(RootCallback* callback, void* arg) {
+ // List of moving roots.
+ typedef std::pair<mirror::Array*, mirror::Array*> ArrayPair;
+ std::list<ArrayPair> moving_roots;
+
+ for (auto it : array_logs_) {
+ mirror::Array* old_root = it.first;
+ if (old_root->IsObjectArray()) {
+ it.second.VisitRoots(callback, arg);
+ }
+ mirror::Array* new_root = down_cast<mirror::Array*>(callback(old_root, arg, 0, kRootUnknown));
+ if (new_root != old_root) {
+ moving_roots.push_back(std::make_pair(old_root, new_root));
+ }
+ }
+
+ // Update array logs with moving roots.
+ for (const ArrayPair& pair : moving_roots) {
+ mirror::Array* old_root = pair.first;
+ mirror::Array* new_root = pair.second;
+ auto old_root_it = array_logs_.find(old_root);
+ CHECK(old_root_it != array_logs_.end());
+ CHECK(array_logs_.find(new_root) == array_logs_.end());
+ array_logs_.insert(std::make_pair(new_root, old_root_it->second));
+ array_logs_.erase(old_root_it);
+ }
+}
+
+void Transaction::VisitStringLogs(RootCallback* callback, void* arg) {
+ for (InternStringLog& log : intern_string_logs_) {
+ log.VisitRoots(callback, arg);
+ }
+}
+
+void Transaction::ObjectLog::Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile) {
+ auto it = field_values_.find(offset.Uint32Value());
+ if (it == field_values_.end()) {
+ ObjectLog::FieldValue field_value;
+ field_value.value = value;
+ field_value.is_volatile = is_volatile;
+ field_value.kind = ObjectLog::k32Bits;
+ field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
+ }
+}
+
+void Transaction::ObjectLog::Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile) {
+ auto it = field_values_.find(offset.Uint32Value());
+ if (it == field_values_.end()) {
+ ObjectLog::FieldValue field_value;
+ field_value.value = value;
+ field_value.is_volatile = is_volatile;
+ field_value.kind = ObjectLog::k64Bits;
+ field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
+ }
+}
+
+void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) {
+ auto it = field_values_.find(offset.Uint32Value());
+ if (it == field_values_.end()) {
+ ObjectLog::FieldValue field_value;
+ field_value.value = reinterpret_cast<uintptr_t>(obj);
+ field_value.is_volatile = is_volatile;
+ field_value.kind = ObjectLog::kReference;
+ field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
+ }
+}
+
+void Transaction::ObjectLog::Undo(mirror::Object* obj) {
+ for (auto& it : field_values_) {
+ // Garbage collector needs to access object's class and array's length. So we don't rollback
+ // these values.
+ MemberOffset field_offset(it.first);
+ if (field_offset.Uint32Value() == mirror::Class::ClassOffset().Uint32Value()) {
+ // Skip Object::class field.
+ continue;
+ }
+ if (obj->IsArrayInstance() &&
+ field_offset.Uint32Value() == mirror::Array::LengthOffset().Uint32Value()) {
+ // Skip Array::length field.
+ continue;
+ }
+ FieldValue& field_value = it.second;
+ UndoFieldWrite(obj, field_offset, field_value);
+ }
+}
+
+void Transaction::ObjectLog::UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset,
+ const FieldValue& field_value) {
+ // TODO We may want to abort a transaction while still being in transaction mode. In this case,
+ // we'd need to disable the check.
+ constexpr bool kCheckTransaction = true;
+ switch (field_value.kind) {
+ case k32Bits:
+ obj->SetField32<false, kCheckTransaction>(field_offset, static_cast<uint32_t>(field_value.value),
+ field_value.is_volatile);
+ break;
+ case k64Bits:
+ obj->SetField64<false, kCheckTransaction>(field_offset, field_value.value,
+ field_value.is_volatile);
+ break;
+ case kReference:
+ obj->SetFieldObject<false, kCheckTransaction>(field_offset,
+ reinterpret_cast<mirror::Object*>(field_value.value),
+ field_value.is_volatile);
+ break;
+ default:
+ LOG(FATAL) << "Unknown value kind " << field_value.kind;
+ break;
+ }
+}
+
+void Transaction::ObjectLog::VisitRoots(RootCallback* callback, void* arg) {
+ for (auto it : field_values_) {
+ FieldValue& field_value = it.second;
+ if (field_value.kind == ObjectLog::kReference) {
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
+ field_value.value = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown));
+ }
+ }
+}
+
+void Transaction::InternStringLog::Undo(InternTable* intern_table) {
+ DCHECK(intern_table != nullptr);
+ switch (string_op_) {
+ case InternStringLog::kInsert: {
+ switch (string_kind_) {
+ case InternStringLog::kStrongString:
+ intern_table->RemoveStrongFromTransaction(str_, hash_code_);
+ break;
+ case InternStringLog::kWeakString:
+ intern_table->RemoveWeakFromTransaction(str_, hash_code_);
+ break;
+ default:
+ LOG(FATAL) << "Unknown interned string kind";
+ break;
+ }
+ break;
+ }
+ case InternStringLog::kRemove: {
+ switch (string_kind_) {
+ case InternStringLog::kStrongString:
+ intern_table->InsertStrongFromTransaction(str_, hash_code_);
+ break;
+ case InternStringLog::kWeakString:
+ intern_table->InsertWeakFromTransaction(str_, hash_code_);
+ break;
+ default:
+ LOG(FATAL) << "Unknown interned string kind";
+ break;
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unknown interned string op";
+ break;
+ }
+}
+
+void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
+ str_ = down_cast<mirror::String*>(callback(str_, arg, 0, kRootInternedString));
+}
+
+void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
+ auto it = array_values_.find(index);
+ if (it == array_values_.end()) {
+ array_values_.insert(std::make_pair(index, value));
+ }
+}
+
+void Transaction::ArrayLog::Undo(mirror::Array* array) {
+ DCHECK(array != nullptr);
+ DCHECK(array->IsArrayInstance());
+ Primitive::Type type = array->GetClass()->GetComponentType()->GetPrimitiveType();
+ for (auto it : array_values_) {
+ UndoArrayWrite(array, type, it.first, it.second);
+ }
+}
+
+void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array, Primitive::Type array_type,
+ size_t index, uint64_t value) {
+ // TODO We may want to abort a transaction while still being in transaction mode. In this case,
+ // we'd need to disable the check.
+ switch (array_type) {
+ case Primitive::kPrimBoolean:
+ array->AsBooleanArray()->SetWithoutChecks<false>(index, static_cast<uint8_t>(value));
+ break;
+ case Primitive::kPrimByte:
+ array->AsByteArray()->SetWithoutChecks<false>(index, static_cast<int8_t>(value));
+ break;
+ case Primitive::kPrimChar:
+ array->AsCharArray()->SetWithoutChecks<false>(index, static_cast<uint16_t>(value));
+ break;
+ case Primitive::kPrimShort:
+ array->AsShortArray()->SetWithoutChecks<false>(index, static_cast<int16_t>(value));
+ break;
+ case Primitive::kPrimInt:
+ array->AsIntArray()->SetWithoutChecks<false>(index, static_cast<int32_t>(value));
+ break;
+ case Primitive::kPrimFloat:
+ array->AsFloatArray()->SetWithoutChecks<false>(index, static_cast<float>(value));
+ break;
+ case Primitive::kPrimLong:
+ array->AsLongArray()->SetWithoutChecks<false>(index, static_cast<int64_t>(value));
+ break;
+ case Primitive::kPrimDouble:
+ array->AsDoubleArray()->SetWithoutChecks<false>(index, static_cast<double>(value));
+ break;
+ case Primitive::kPrimNot: {
+ mirror::ObjectArray<mirror::Object>* obj_array = array->AsObjectArray<mirror::Object>();
+ obj_array->SetWithoutChecks<false>(index, reinterpret_cast<mirror::Object*>(
+ static_cast<uintptr_t>(value)));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unsupported type " << array_type;
+ }
+}
+
+void Transaction::ArrayLog::VisitRoots(RootCallback* callback, void* arg) {
+ for (auto& it : array_values_) {
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(it.second));
+ it.second = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown));
+ }
+}
+
+} // namespace art
diff --git a/runtime/transaction.h b/runtime/transaction.h
new file mode 100644
index 0000000..68f9540
--- /dev/null
+++ b/runtime/transaction.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_TRANSACTION_H_
+#define ART_RUNTIME_TRANSACTION_H_
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "locks.h"
+#include "offsets.h"
+#include "primitive.h"
+#include "object_callbacks.h"
+#include "safe_map.h"
+
+#include <list>
+#include <map>
+
+namespace art {
+namespace mirror {
+class Array;
+class Object;
+class String;
+}
+class InternTable;
+
+class Transaction {
+ public:
+ Transaction();
+ ~Transaction();
+
+ // Record object field changes.
+ void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
+ bool is_volatile)
+ LOCKS_EXCLUDED(log_lock_);
+ void RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
+ bool is_volatile)
+ LOCKS_EXCLUDED(log_lock_);
+ void RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
+ mirror::Object* value, bool is_volatile)
+ LOCKS_EXCLUDED(log_lock_);
+
+ // Record array change.
+ void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value)
+ LOCKS_EXCLUDED(log_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Record intern string table changes.
+ void RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+ LOCKS_EXCLUDED(log_lock_);
+ void RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+ LOCKS_EXCLUDED(log_lock_);
+ void RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+ LOCKS_EXCLUDED(log_lock_);
+ void RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+ LOCKS_EXCLUDED(log_lock_);
+
+ // Abort transaction by undoing all recorded changes.
+ void Abort()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(log_lock_);
+
+ void VisitRoots(RootCallback* callback, void* arg)
+ LOCKS_EXCLUDED(log_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+ class ObjectLog {
+ public:
+ void Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile);
+ void Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile);
+ void LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile);
+
+ void Undo(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitRoots(RootCallback* callback, void* arg);
+
+ size_t Size() const {
+ return field_values_.size();
+ }
+
+ private:
+ enum FieldValueKind {
+ k32Bits,
+ k64Bits,
+ kReference
+ };
+ struct FieldValue {
+ // TODO use JValue instead ?
+ uint64_t value;
+ FieldValueKind kind;
+ bool is_volatile;
+ };
+
+ void UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset,
+ const FieldValue& field_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Maps field's offset to its value.
+ std::map<uint32_t, FieldValue> field_values_;
+ };
+
+ class ArrayLog {
+ public:
+ void LogValue(size_t index, uint64_t value);
+
+ void Undo(mirror::Array* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitRoots(RootCallback* callback, void* arg);
+
+ size_t Size() const {
+ return array_values_.size();
+ }
+
+ private:
+ void UndoArrayWrite(mirror::Array* array, Primitive::Type array_type, size_t index,
+ uint64_t value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Maps index to value.
+ // TODO use JValue instead ?
+ std::map<size_t, uint64_t> array_values_;
+ };
+
+ class InternStringLog {
+ public:
+ enum StringKind {
+ kStrongString,
+ kWeakString
+ };
+ enum StringOp {
+ kInsert,
+ kRemove
+ };
+ InternStringLog(mirror::String* s, uint32_t hash_code, StringKind kind, StringOp op)
+ : str_(s), hash_code_(hash_code), string_kind_(kind), string_op_(op) {
+ }
+
+ void Undo(InternTable* intern_table) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void VisitRoots(RootCallback* callback, void* arg);
+
+ private:
+ mirror::String* str_;
+ uint32_t hash_code_;
+ StringKind string_kind_;
+ StringOp string_op_;
+ };
+
+ void LogInternedString(InternStringLog& log)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+ LOCKS_EXCLUDED(log_lock_);
+
+ void UndoObjectModifications()
+ EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void UndoArrayModifications()
+ EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void UndoInternStringTableModifications()
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(log_lock_);
+
+ void VisitObjectLogs(RootCallback* callback, void* arg)
+ EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitArrayLogs(RootCallback* callback, void* arg)
+ EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitStringLogs(RootCallback* callback, void* arg)
+ EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ Mutex log_lock_ ACQUIRED_AFTER(Locks::intern_table_lock_);
+ std::map<mirror::Object*, ObjectLog> object_logs_ GUARDED_BY(log_lock_);
+ std::map<mirror::Array*, ArrayLog> array_logs_ GUARDED_BY(log_lock_);
+ std::list<InternStringLog> intern_string_logs_ GUARDED_BY(log_lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(Transaction);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_TRANSACTION_H_
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
new file mode 100644
index 0000000..dcfa24b
--- /dev/null
+++ b/runtime/transaction_test.cc
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common_test.h"
+#include "invoke_arg_array_builder.h"
+#include "mirror/array-inl.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/art_method-inl.h"
+#include "transaction.h"
+
+namespace art {
+
+class TransactionTest : public CommonTest {
+};
+
+TEST_F(TransactionTest, Object_class) {
+ ScopedObjectAccess soa(Thread::Current());
+ SirtRef<mirror::Class> sirt_klass(soa.Self(),
+ class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+ ASSERT_TRUE(sirt_obj.get() != nullptr);
+ ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+ Runtime::Current()->ExitTransactionMode();
+
+ // Aborting transaction must not clear the Object::class field.
+ transaction.Abort();
+ EXPECT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+}
+
+TEST_F(TransactionTest, Object_monitor) {
+ ScopedObjectAccess soa(Thread::Current());
+ SirtRef<mirror::Class> sirt_klass(soa.Self(),
+ class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+ ASSERT_TRUE(sirt_obj.get() != nullptr);
+ ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+
+ // Lock object's monitor outside the transaction.
+ sirt_obj->MonitorEnter(soa.Self());
+ uint32_t old_lock_word = sirt_obj->GetLockWord().GetValue();
+
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ // Unlock object's monitor inside the transaction.
+ sirt_obj->MonitorExit(soa.Self());
+ uint32_t new_lock_word = sirt_obj->GetLockWord().GetValue();
+ Runtime::Current()->ExitTransactionMode();
+
+ // Aborting transaction must not clear the Object::class field.
+ transaction.Abort();
+ uint32_t aborted_lock_word = sirt_obj->GetLockWord().GetValue();
+ EXPECT_NE(old_lock_word, new_lock_word);
+ EXPECT_EQ(aborted_lock_word, new_lock_word);
+}
+
+TEST_F(TransactionTest, Array_length) {
+ ScopedObjectAccess soa(Thread::Current());
+ SirtRef<mirror::Class> sirt_klass(soa.Self(),
+ class_linker_->FindSystemClass("[Ljava/lang/Object;"));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+
+ constexpr int32_t kArraySize = 2;
+
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+
+ // Allocate an array during transaction.
+ SirtRef<mirror::Array> sirt_obj(soa.Self(),
+ mirror::Array::Alloc<false>(soa.Self(), sirt_klass.get(), kArraySize));
+ ASSERT_TRUE(sirt_obj.get() != nullptr);
+ ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+ Runtime::Current()->ExitTransactionMode();
+
+ // Aborting transaction must not clear the Object::class field.
+ transaction.Abort();
+ EXPECT_EQ(sirt_obj->GetLength(), kArraySize);
+}
+
+TEST_F(TransactionTest, StaticFieldsTest) {
+ ScopedObjectAccess soa(Thread::Current());
+ SirtRef<mirror::ClassLoader> class_loader(
+ soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+ ASSERT_TRUE(class_loader.get() != nullptr);
+
+ SirtRef<mirror::Class> sirt_klass(soa.Self(),
+ class_linker_->FindClass("LStaticFieldsTest;", class_loader));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ class_linker_->EnsureInitialized(sirt_klass, true, true);
+ ASSERT_TRUE(sirt_klass->IsInitialized());
+
+ // Lookup fields.
+ mirror::ArtField* booleanField = sirt_klass->FindDeclaredStaticField("booleanField", "Z");
+ ASSERT_TRUE(booleanField != nullptr);
+ ASSERT_EQ(FieldHelper(booleanField).GetTypeAsPrimitiveType(), Primitive::kPrimBoolean);
+ ASSERT_EQ(booleanField->GetBoolean(sirt_klass.get()), false);
+
+ mirror::ArtField* byteField = sirt_klass->FindDeclaredStaticField("byteField", "B");
+ ASSERT_TRUE(byteField != nullptr);
+ ASSERT_EQ(FieldHelper(byteField).GetTypeAsPrimitiveType(), Primitive::kPrimByte);
+ ASSERT_EQ(byteField->GetByte(sirt_klass.get()), 0);
+
+ mirror::ArtField* charField = sirt_klass->FindDeclaredStaticField("charField", "C");
+ ASSERT_TRUE(charField != nullptr);
+ ASSERT_EQ(FieldHelper(charField).GetTypeAsPrimitiveType(), Primitive::kPrimChar);
+ ASSERT_EQ(charField->GetChar(sirt_klass.get()), 0u);
+
+ mirror::ArtField* shortField = sirt_klass->FindDeclaredStaticField("shortField", "S");
+ ASSERT_TRUE(shortField != nullptr);
+ ASSERT_EQ(FieldHelper(shortField).GetTypeAsPrimitiveType(), Primitive::kPrimShort);
+ ASSERT_EQ(shortField->GetShort(sirt_klass.get()), 0);
+
+ mirror::ArtField* intField = sirt_klass->FindDeclaredStaticField("intField", "I");
+ ASSERT_TRUE(intField != nullptr);
+ ASSERT_EQ(FieldHelper(intField).GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ ASSERT_EQ(intField->GetInt(sirt_klass.get()), 0);
+
+ mirror::ArtField* longField = sirt_klass->FindDeclaredStaticField("longField", "J");
+ ASSERT_TRUE(longField != nullptr);
+ ASSERT_EQ(FieldHelper(longField).GetTypeAsPrimitiveType(), Primitive::kPrimLong);
+ ASSERT_EQ(longField->GetLong(sirt_klass.get()), static_cast<int64_t>(0));
+
+ mirror::ArtField* floatField = sirt_klass->FindDeclaredStaticField("floatField", "F");
+ ASSERT_TRUE(floatField != nullptr);
+ ASSERT_EQ(FieldHelper(floatField).GetTypeAsPrimitiveType(), Primitive::kPrimFloat);
+ ASSERT_EQ(floatField->GetFloat(sirt_klass.get()), static_cast<float>(0.0f));
+
+ mirror::ArtField* doubleField = sirt_klass->FindDeclaredStaticField("doubleField", "D");
+ ASSERT_TRUE(doubleField != nullptr);
+ ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
+ ASSERT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast<double>(0.0));
+
+ mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField", "Ljava/lang/Object;");
+ ASSERT_TRUE(objectField != nullptr);
+ ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+ ASSERT_EQ(objectField->GetObject(sirt_klass.get()), nullptr);
+
+ // Create a java.lang.Object instance to set objectField.
+ SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ ASSERT_TRUE(object_klass.get() != nullptr);
+ SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+ ASSERT_TRUE(sirt_obj.get() != nullptr);
+ ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+
+ // Modify fields inside transaction and abort it.
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ booleanField->SetBoolean<true>(sirt_klass.get(), true);
+ byteField->SetByte<true>(sirt_klass.get(), 1);
+ charField->SetChar<true>(sirt_klass.get(), 1u);
+ shortField->SetShort<true>(sirt_klass.get(), 1);
+ intField->SetInt<true>(sirt_klass.get(), 1);
+ longField->SetLong<true>(sirt_klass.get(), 1);
+ floatField->SetFloat<true>(sirt_klass.get(), 1.0);
+ doubleField->SetDouble<true>(sirt_klass.get(), 1.0);
+ objectField->SetObject<true>(sirt_klass.get(), sirt_obj.get());
+ Runtime::Current()->ExitTransactionMode();
+ transaction.Abort();
+
+ // Check values have properly been restored to their original (default) value.
+ EXPECT_EQ(booleanField->GetBoolean(sirt_klass.get()), false);
+ EXPECT_EQ(byteField->GetByte(sirt_klass.get()), 0);
+ EXPECT_EQ(charField->GetChar(sirt_klass.get()), 0u);
+ EXPECT_EQ(shortField->GetShort(sirt_klass.get()), 0);
+ EXPECT_EQ(intField->GetInt(sirt_klass.get()), 0);
+ EXPECT_EQ(longField->GetLong(sirt_klass.get()), static_cast<int64_t>(0));
+ EXPECT_EQ(floatField->GetFloat(sirt_klass.get()), static_cast<float>(0.0f));
+ EXPECT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast<double>(0.0));
+ EXPECT_EQ(objectField->GetObject(sirt_klass.get()), nullptr);
+}
+
+TEST_F(TransactionTest, InstanceFieldsTest) {
+ ScopedObjectAccess soa(Thread::Current());
+ SirtRef<mirror::ClassLoader> class_loader(
+ soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+ ASSERT_TRUE(class_loader.get() != nullptr);
+
+ SirtRef<mirror::Class> sirt_klass(soa.Self(),
+ class_linker_->FindClass("LInstanceFieldsTest;", class_loader));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ class_linker_->EnsureInitialized(sirt_klass, true, true);
+ ASSERT_TRUE(sirt_klass->IsInitialized());
+
+ // Allocate an InstanceFieldTest object.
+ SirtRef<mirror::Object> sirt_instance(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+ ASSERT_TRUE(sirt_instance.get() != nullptr);
+
+ // Lookup fields.
+ mirror::ArtField* booleanField = sirt_klass->FindDeclaredInstanceField("booleanField", "Z");
+ ASSERT_TRUE(booleanField != nullptr);
+ ASSERT_EQ(FieldHelper(booleanField).GetTypeAsPrimitiveType(), Primitive::kPrimBoolean);
+ ASSERT_EQ(booleanField->GetBoolean(sirt_instance.get()), false);
+
+ mirror::ArtField* byteField = sirt_klass->FindDeclaredInstanceField("byteField", "B");
+ ASSERT_TRUE(byteField != nullptr);
+ ASSERT_EQ(FieldHelper(byteField).GetTypeAsPrimitiveType(), Primitive::kPrimByte);
+ ASSERT_EQ(byteField->GetByte(sirt_instance.get()), 0);
+
+ mirror::ArtField* charField = sirt_klass->FindDeclaredInstanceField("charField", "C");
+ ASSERT_TRUE(charField != nullptr);
+ ASSERT_EQ(FieldHelper(charField).GetTypeAsPrimitiveType(), Primitive::kPrimChar);
+ ASSERT_EQ(charField->GetChar(sirt_instance.get()), 0u);
+
+ mirror::ArtField* shortField = sirt_klass->FindDeclaredInstanceField("shortField", "S");
+ ASSERT_TRUE(shortField != nullptr);
+ ASSERT_EQ(FieldHelper(shortField).GetTypeAsPrimitiveType(), Primitive::kPrimShort);
+ ASSERT_EQ(shortField->GetShort(sirt_instance.get()), 0);
+
+ mirror::ArtField* intField = sirt_klass->FindDeclaredInstanceField("intField", "I");
+ ASSERT_TRUE(intField != nullptr);
+ ASSERT_EQ(FieldHelper(intField).GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ ASSERT_EQ(intField->GetInt(sirt_instance.get()), 0);
+
+ mirror::ArtField* longField = sirt_klass->FindDeclaredInstanceField("longField", "J");
+ ASSERT_TRUE(longField != nullptr);
+ ASSERT_EQ(FieldHelper(longField).GetTypeAsPrimitiveType(), Primitive::kPrimLong);
+ ASSERT_EQ(longField->GetLong(sirt_instance.get()), static_cast<int64_t>(0));
+
+ mirror::ArtField* floatField = sirt_klass->FindDeclaredInstanceField("floatField", "F");
+ ASSERT_TRUE(floatField != nullptr);
+ ASSERT_EQ(FieldHelper(floatField).GetTypeAsPrimitiveType(), Primitive::kPrimFloat);
+ ASSERT_EQ(floatField->GetFloat(sirt_instance.get()), static_cast<float>(0.0f));
+
+ mirror::ArtField* doubleField = sirt_klass->FindDeclaredInstanceField("doubleField", "D");
+ ASSERT_TRUE(doubleField != nullptr);
+ ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
+ ASSERT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast<double>(0.0));
+
+ mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField", "Ljava/lang/Object;");
+ ASSERT_TRUE(objectField != nullptr);
+ ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+ ASSERT_EQ(objectField->GetObject(sirt_instance.get()), nullptr);
+
+ // Create a java.lang.Object instance to set objectField.
+ SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ ASSERT_TRUE(object_klass.get() != nullptr);
+ SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+ ASSERT_TRUE(sirt_obj.get() != nullptr);
+ ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+
+ // Modify fields inside transaction and abort it.
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ booleanField->SetBoolean<true>(sirt_instance.get(), true);
+ byteField->SetByte<true>(sirt_instance.get(), 1);
+ charField->SetChar<true>(sirt_instance.get(), 1u);
+ shortField->SetShort<true>(sirt_instance.get(), 1);
+ intField->SetInt<true>(sirt_instance.get(), 1);
+ longField->SetLong<true>(sirt_instance.get(), 1);
+ floatField->SetFloat<true>(sirt_instance.get(), 1.0);
+ doubleField->SetDouble<true>(sirt_instance.get(), 1.0);
+ objectField->SetObject<true>(sirt_instance.get(), sirt_obj.get());
+ Runtime::Current()->ExitTransactionMode();
+ transaction.Abort();
+
+ // Check values have properly been restored to their original (default) value.
+ EXPECT_EQ(booleanField->GetBoolean(sirt_instance.get()), false);
+ EXPECT_EQ(byteField->GetByte(sirt_instance.get()), 0);
+ EXPECT_EQ(charField->GetChar(sirt_instance.get()), 0u);
+ EXPECT_EQ(shortField->GetShort(sirt_instance.get()), 0);
+ EXPECT_EQ(intField->GetInt(sirt_instance.get()), 0);
+ EXPECT_EQ(longField->GetLong(sirt_instance.get()), static_cast<int64_t>(0));
+ EXPECT_EQ(floatField->GetFloat(sirt_instance.get()), static_cast<float>(0.0f));
+ EXPECT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast<double>(0.0));
+ EXPECT_EQ(objectField->GetObject(sirt_instance.get()), nullptr);
+}
+
+
+TEST_F(TransactionTest, StaticArrayFieldsTest) {
+ ScopedObjectAccess soa(Thread::Current());
+ SirtRef<mirror::ClassLoader> class_loader(
+ soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+ ASSERT_TRUE(class_loader.get() != nullptr);
+
+ SirtRef<mirror::Class> sirt_klass(soa.Self(),
+ class_linker_->FindClass("LStaticArrayFieldsTest;", class_loader));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ class_linker_->EnsureInitialized(sirt_klass, true, true);
+ ASSERT_TRUE(sirt_klass->IsInitialized());
+
+ // Lookup fields.
+ mirror::ArtField* booleanArrayField = sirt_klass->FindDeclaredStaticField("booleanArrayField", "[Z");
+ ASSERT_TRUE(booleanArrayField != nullptr);
+ mirror::BooleanArray* booleanArray = booleanArrayField->GetObject(sirt_klass.get())->AsBooleanArray();
+ ASSERT_TRUE(booleanArray != nullptr);
+ ASSERT_EQ(booleanArray->GetLength(), 1);
+ ASSERT_EQ(booleanArray->GetWithoutChecks(0), false);
+
+ mirror::ArtField* byteArrayField = sirt_klass->FindDeclaredStaticField("byteArrayField", "[B");
+ ASSERT_TRUE(byteArrayField != nullptr);
+ mirror::ByteArray* byteArray = byteArrayField->GetObject(sirt_klass.get())->AsByteArray();
+ ASSERT_TRUE(byteArray != nullptr);
+ ASSERT_EQ(byteArray->GetLength(), 1);
+ ASSERT_EQ(byteArray->GetWithoutChecks(0), 0);
+
+ mirror::ArtField* charArrayField = sirt_klass->FindDeclaredStaticField("charArrayField", "[C");
+ ASSERT_TRUE(charArrayField != nullptr);
+ mirror::CharArray* charArray = charArrayField->GetObject(sirt_klass.get())->AsCharArray();
+ ASSERT_TRUE(charArray != nullptr);
+ ASSERT_EQ(charArray->GetLength(), 1);
+ ASSERT_EQ(charArray->GetWithoutChecks(0), 0u);
+
+ mirror::ArtField* shortArrayField = sirt_klass->FindDeclaredStaticField("shortArrayField", "[S");
+ ASSERT_TRUE(shortArrayField != nullptr);
+ mirror::ShortArray* shortArray = shortArrayField->GetObject(sirt_klass.get())->AsShortArray();
+ ASSERT_TRUE(shortArray != nullptr);
+ ASSERT_EQ(shortArray->GetLength(), 1);
+ ASSERT_EQ(shortArray->GetWithoutChecks(0), 0);
+
+ mirror::ArtField* intArrayField = sirt_klass->FindDeclaredStaticField("intArrayField", "[I");
+ ASSERT_TRUE(intArrayField != nullptr);
+ mirror::IntArray* intArray = intArrayField->GetObject(sirt_klass.get())->AsIntArray();
+ ASSERT_TRUE(intArray != nullptr);
+ ASSERT_EQ(intArray->GetLength(), 1);
+ ASSERT_EQ(intArray->GetWithoutChecks(0), 0);
+
+ mirror::ArtField* longArrayField = sirt_klass->FindDeclaredStaticField("longArrayField", "[J");
+ ASSERT_TRUE(longArrayField != nullptr);
+ mirror::LongArray* longArray = longArrayField->GetObject(sirt_klass.get())->AsLongArray();
+ ASSERT_TRUE(longArray != nullptr);
+ ASSERT_EQ(longArray->GetLength(), 1);
+ ASSERT_EQ(longArray->GetWithoutChecks(0), static_cast<int64_t>(0));
+
+ mirror::ArtField* floatArrayField = sirt_klass->FindDeclaredStaticField("floatArrayField", "[F");
+ ASSERT_TRUE(floatArrayField != nullptr);
+ mirror::FloatArray* floatArray = floatArrayField->GetObject(sirt_klass.get())->AsFloatArray();
+ ASSERT_TRUE(floatArray != nullptr);
+ ASSERT_EQ(floatArray->GetLength(), 1);
+ ASSERT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f));
+
+ mirror::ArtField* doubleArrayField = sirt_klass->FindDeclaredStaticField("doubleArrayField", "[D");
+ ASSERT_TRUE(doubleArrayField != nullptr);
+ mirror::DoubleArray* doubleArray = doubleArrayField->GetObject(sirt_klass.get())->AsDoubleArray();
+ ASSERT_TRUE(doubleArray != nullptr);
+ ASSERT_EQ(doubleArray->GetLength(), 1);
+ ASSERT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
+
+ mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField", "[Ljava/lang/Object;");
+ ASSERT_TRUE(objectArrayField != nullptr);
+ mirror::ObjectArray<mirror::Object>* objectArray = objectArrayField->GetObject(sirt_klass.get())->AsObjectArray<mirror::Object>();
+ ASSERT_TRUE(objectArray != nullptr);
+ ASSERT_EQ(objectArray->GetLength(), 1);
+ ASSERT_EQ(objectArray->GetWithoutChecks(0), nullptr);
+
+ // Create a java.lang.Object instance to set objectField.
+ SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ ASSERT_TRUE(object_klass.get() != nullptr);
+ SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+ ASSERT_TRUE(sirt_obj.get() != nullptr);
+ ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+
+ // Modify fields inside transaction and abort it.
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ booleanArray->SetWithoutChecks<true>(0, true);
+ byteArray->SetWithoutChecks<true>(0, 1);
+ charArray->SetWithoutChecks<true>(0, 1u);
+ shortArray->SetWithoutChecks<true>(0, 1);
+ intArray->SetWithoutChecks<true>(0, 1);
+ longArray->SetWithoutChecks<true>(0, 1);
+ floatArray->SetWithoutChecks<true>(0, 1.0);
+ doubleArray->SetWithoutChecks<true>(0, 1.0);
+ objectArray->SetWithoutChecks<true>(0, sirt_obj.get());
+ Runtime::Current()->ExitTransactionMode();
+ transaction.Abort();
+
+ // Check values have properly been restored to their original (default) value.
+ EXPECT_EQ(booleanArray->GetWithoutChecks(0), false);
+ EXPECT_EQ(byteArray->GetWithoutChecks(0), 0);
+ EXPECT_EQ(charArray->GetWithoutChecks(0), 0u);
+ EXPECT_EQ(shortArray->GetWithoutChecks(0), 0);
+ EXPECT_EQ(intArray->GetWithoutChecks(0), 0);
+ EXPECT_EQ(longArray->GetWithoutChecks(0), static_cast<int64_t>(0));
+ EXPECT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f));
+ EXPECT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
+ EXPECT_EQ(objectArray->GetWithoutChecks(0), nullptr);
+}
+
+TEST_F(TransactionTest, EmptyClass) {
+ ScopedObjectAccess soa(Thread::Current());
+ SirtRef<mirror::ClassLoader> class_loader(
+ soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+ ASSERT_TRUE(class_loader.get() != nullptr);
+
+ SirtRef<mirror::Class> sirt_klass(soa.Self(),
+ class_linker_->FindClass("LTransaction$EmptyStatic;", class_loader));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ class_linker_->VerifyClass(sirt_klass);
+ ASSERT_TRUE(sirt_klass->IsVerified());
+
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ class_linker_->EnsureInitialized(sirt_klass, true, true);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+}
+
+TEST_F(TransactionTest, StaticFieldClass) {
+ ScopedObjectAccess soa(Thread::Current());
+ SirtRef<mirror::ClassLoader> class_loader(
+ soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+ ASSERT_TRUE(class_loader.get() != nullptr);
+
+ SirtRef<mirror::Class> sirt_klass(soa.Self(),
+ class_linker_->FindClass("LTransaction$StaticFieldClass;", class_loader));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ class_linker_->VerifyClass(sirt_klass);
+ ASSERT_TRUE(sirt_klass->IsVerified());
+
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ class_linker_->EnsureInitialized(sirt_klass, true, true);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+}
+
+TEST_F(TransactionTest, BlacklistedClass) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject jclass_loader = LoadDex("Transaction");
+ SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
+ soa.Decode<mirror::ClassLoader*>(jclass_loader));
+ ASSERT_TRUE(class_loader.get() != nullptr);
+
+ // Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will
+ // be thrown by class initialization due to native call.
+ SirtRef<mirror::Class> sirt_klass(soa.Self(),
+ class_linker_->FindSystemClass("Ljava/lang/ExceptionInInitializerError;"));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ class_linker_->VerifyClass(sirt_klass);
+ ASSERT_TRUE(sirt_klass->IsVerified());
+ sirt_klass.reset(class_linker_->FindSystemClass("Ljava/lang/InternalError;"));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ class_linker_->VerifyClass(sirt_klass);
+ ASSERT_TRUE(sirt_klass->IsVerified());
+
+ // Load and verify Transaction$NativeSupport used in class initialization.
+ sirt_klass.reset(class_linker_->FindClass("LTransaction$NativeSupport;", class_loader));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ class_linker_->VerifyClass(sirt_klass);
+ ASSERT_TRUE(sirt_klass->IsVerified());
+
+ sirt_klass.reset(class_linker_->FindClass("LTransaction$BlacklistedClass;", class_loader));
+ ASSERT_TRUE(sirt_klass.get() != nullptr);
+ class_linker_->VerifyClass(sirt_klass);
+ ASSERT_TRUE(sirt_klass->IsVerified());
+
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ class_linker_->EnsureInitialized(sirt_klass, true, true);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+}
+
+
+} // namespace art