Transaction support for MethodType in dex cache.
Test: TransactionTest.ResolveMethodType
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing --interpreter
Bug: 191765508
Change-Id: I8c80e29fb84d50ca782d05db1bc4634fc002c726
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 1ca4202..0553f4f 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -202,10 +202,27 @@
DCHECK(resolved != nullptr);
GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store(
MethodTypeDexCachePair(resolved, proto_idx.index_), std::memory_order_relaxed);
+ Runtime* const runtime = Runtime::Current();
+ if (UNLIKELY(runtime->IsActiveTransaction())) {
+ DCHECK(runtime->IsAotCompiler());
+ runtime->RecordResolveMethodType(this, proto_idx);
+ }
// TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
WriteBarrier::ForEveryFieldWrite(this);
}
+inline void DexCache::ClearMethodType(dex::ProtoIndex proto_idx) {
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ uint32_t slot_idx = MethodTypeSlotIndex(proto_idx);
+ MethodTypeDexCacheType* slot = &GetResolvedMethodTypes()[slot_idx];
+ // This is racy but should only be called from the transactional interpreter.
+ if (slot->load(std::memory_order_relaxed).index == proto_idx.index_) {
+ MethodTypeDexCachePair cleared(nullptr,
+ MethodTypeDexCachePair::InvalidIndexForSlot(proto_idx.index_));
+ slot->store(cleared, std::memory_order_relaxed);
+ }
+}
+
inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) {
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index ea52785..03ff221 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -316,6 +316,10 @@
void SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Clear a method type for proto_idx, used to undo method type resolution
+ // in aborted transactions to make sure the method type isn't kept live.
+ void ClearMethodType(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_);
// Attempts to bind |call_site_idx| to the call site |resolved|. The
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index dc4acc0..25bfdc7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -2759,6 +2759,13 @@
GetTransaction()->RecordResolveString(dex_cache, string_idx);
}
+void Runtime::RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache,
+ dex::ProtoIndex proto_idx) const {
+ DCHECK(IsAotCompiler());
+ DCHECK(IsActiveTransaction());
+ GetTransaction()->RecordResolveMethodType(dex_cache, proto_idx);
+}
+
void Runtime::SetFaultMessage(const std::string& message) {
std::string* new_msg = new std::string(message);
std::string* cur_msg = fault_message_.exchange(new_msg);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 1413b98..59f4d19 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -578,6 +578,8 @@
REQUIRES(Locks::intern_table_lock_);
void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ void RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache, dex::ProtoIndex proto_idx) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
void SetFaultMessage(const std::string& message);
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 0293d23..0ad7a78 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -268,6 +268,15 @@
resolve_string_logs_.emplace_back(dex_cache, string_idx);
}
+void Transaction::RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache,
+ dex::ProtoIndex proto_idx) {
+ DCHECK(dex_cache != nullptr);
+ DCHECK_LT(proto_idx.index_, dex_cache->GetDexFile()->NumProtoIds());
+ MutexLock mu(Thread::Current(), log_lock_);
+ DCHECK(assert_no_new_records_reason_ == nullptr) << assert_no_new_records_reason_;
+ resolve_method_type_logs_.emplace_back(dex_cache, proto_idx);
+}
+
void Transaction::RecordStrongStringInsertion(ObjPtr<mirror::String> s) {
InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kInsert);
LogInternedString(std::move(log));
@@ -306,6 +315,7 @@
UndoArrayModifications();
UndoInternStringTableModifications();
UndoResolveStringModifications();
+ UndoResolveMethodTypeModifications();
rolling_back_ = false;
}
@@ -344,6 +354,13 @@
resolve_string_logs_.clear();
}
+void Transaction::UndoResolveMethodTypeModifications() {
+ for (ResolveMethodTypeLog& method_type_log : resolve_method_type_logs_) {
+ method_type_log.Undo();
+ }
+ resolve_method_type_logs_.clear();
+}
+
void Transaction::VisitRoots(RootVisitor* visitor) {
MutexLock mu(Thread::Current(), log_lock_);
visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&root_), RootInfo(kRootUnknown));
@@ -351,6 +368,7 @@
VisitArrayLogs(visitor);
VisitInternStringLogs(visitor);
VisitResolveStringLogs(visitor);
+ VisitResolveMethodTypeLogs(visitor);
}
void Transaction::VisitObjectLogs(RootVisitor* visitor) {
@@ -420,6 +438,12 @@
}
}
+void Transaction::VisitResolveMethodTypeLogs(RootVisitor* visitor) {
+ for (ResolveMethodTypeLog& log : resolve_method_type_logs_) {
+ log.VisitRoots(visitor);
+ }
+}
+
void Transaction::ObjectLog::LogBooleanValue(MemberOffset offset, uint8_t value, bool is_volatile) {
LogValue(ObjectLog::kBoolean, offset, value, is_volatile);
}
@@ -636,6 +660,22 @@
dex_cache_.VisitRoot(visitor, RootInfo(kRootVMInternal));
}
+void Transaction::ResolveMethodTypeLog::Undo() const {
+ dex_cache_.Read()->ClearMethodType(proto_idx_);
+}
+
+Transaction::ResolveMethodTypeLog::ResolveMethodTypeLog(ObjPtr<mirror::DexCache> dex_cache,
+ dex::ProtoIndex proto_idx)
+ : dex_cache_(dex_cache),
+ proto_idx_(proto_idx) {
+ DCHECK(dex_cache != nullptr);
+ DCHECK_LT(proto_idx_.index_, dex_cache->GetDexFile()->NumProtoIds());
+}
+
+void Transaction::ResolveMethodTypeLog::VisitRoots(RootVisitor* visitor) {
+ dex_cache_.VisitRoot(visitor, RootInfo(kRootVMInternal));
+}
+
Transaction::InternStringLog::InternStringLog(ObjPtr<mirror::String> s,
StringKind kind,
StringOp op)
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 2fd0e2c..ece548c 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -130,6 +130,11 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!log_lock_);
+ // Record resolve method type.
+ void RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache, dex::ProtoIndex proto_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!log_lock_);
+
// Abort transaction by undoing all recorded changes.
void Rollback()
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -275,6 +280,21 @@
DISALLOW_COPY_AND_ASSIGN(ResolveStringLog);
};
+ class ResolveMethodTypeLog : public ValueObject {
+ public:
+ ResolveMethodTypeLog(ObjPtr<mirror::DexCache> dex_cache, dex::ProtoIndex proto_idx);
+
+ void Undo() const REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ GcRoot<mirror::DexCache> dex_cache_;
+ const dex::ProtoIndex proto_idx_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResolveMethodTypeLog);
+ };
+
void LogInternedString(InternStringLog&& log)
REQUIRES(Locks::intern_table_lock_)
REQUIRES(!log_lock_);
@@ -292,6 +312,9 @@
void UndoResolveStringModifications()
REQUIRES(log_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void UndoResolveMethodTypeModifications()
+ REQUIRES(log_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
void VisitObjectLogs(RootVisitor* visitor)
REQUIRES(log_lock_)
@@ -305,6 +328,9 @@
void VisitResolveStringLogs(RootVisitor* visitor)
REQUIRES(log_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void VisitResolveMethodTypeLogs(RootVisitor* visitor)
+ REQUIRES(log_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
const std::string& GetAbortMessage() REQUIRES(!log_lock_);
@@ -313,6 +339,7 @@
std::map<mirror::Array*, ArrayLog> array_logs_ GUARDED_BY(log_lock_);
std::list<InternStringLog> intern_string_logs_ GUARDED_BY(log_lock_);
std::list<ResolveStringLog> resolve_string_logs_ GUARDED_BY(log_lock_);
+ std::list<ResolveMethodTypeLog> resolve_method_type_logs_ GUARDED_BY(log_lock_);
bool aborted_ GUARDED_BY(log_lock_);
bool rolling_back_; // Single thread, no race.
gc::Heap* const heap_;
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index f606d02..4c2c4c2 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -531,6 +531,39 @@
ASSERT_FALSE(soa.Self()->IsExceptionPending());
}
+// Tests rolling back resolved method types in dex cache.
+TEST_F(TransactionTest, ResolveMethodType) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
+ ASSERT_TRUE(class_loader != nullptr);
+
+ Handle<mirror::Class> h_klass(
+ hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction;", class_loader)));
+ ASSERT_TRUE(h_klass != nullptr);
+
+ Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(h_klass->GetDexCache()));
+ ASSERT_TRUE(h_dex_cache != nullptr);
+ const DexFile* const dex_file = h_dex_cache->GetDexFile();
+ ASSERT_TRUE(dex_file != nullptr);
+
+ ASSERT_NE(dex_file->NumProtoIds(), 0u);
+ dex::ProtoIndex proto_index(0u);
+ ASSERT_TRUE(h_dex_cache->GetResolvedMethodType(proto_index) == nullptr);
+
+ // Do the transaction, then roll back.
+ EnterTransactionMode();
+ ObjPtr<mirror::MethodType> method_type =
+ class_linker_->ResolveMethodType(soa.Self(), proto_index, h_dex_cache, class_loader);
+ ASSERT_TRUE(method_type != nullptr);
+ // Make sure the method type was recorded in the dex cache.
+ ASSERT_TRUE(h_dex_cache->GetResolvedMethodType(proto_index) == method_type);
+ RollbackAndExitTransactionMode();
+ // Check that the method type was removed from the dex cache.
+ ASSERT_TRUE(h_dex_cache->GetResolvedMethodType(proto_index) == nullptr);
+}
+
// Tests successful class initialization without class initializer.
TEST_F(TransactionTest, EmptyClass) {
ScopedObjectAccess soa(Thread::Current());