Add transactions for string resolve
Fixes a bug where resolved strings can be left in the dex cache after
a transaction is rolled back even though the interned string was
removed.
Added test in transaction_test.
Bug: 31239436
Test: test-art-host
Change-Id: I42c67bcefeae8db134cde34c480261f52db4102e
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index a3071b7..220979a 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -44,13 +44,29 @@
inline void DexCache::SetResolvedString(uint32_t string_idx, mirror::String* resolved) {
DCHECK_LT(string_idx % NumStrings(), NumStrings());
- // TODO default transaction support.
- StringDexCachePair idx_ptr;
- idx_ptr.string_index = string_idx;
- idx_ptr.string_pointer = GcRoot<String>(resolved);
- GetStrings()[string_idx % NumStrings()].store(idx_ptr, std::memory_order_relaxed);
+ GetStrings()[string_idx % NumStrings()].store(
+ StringDexCachePair(resolved, string_idx),
+ std::memory_order_relaxed);
+ Runtime* const runtime = Runtime::Current();
+ if (UNLIKELY(runtime->IsActiveTransaction())) {
+ DCHECK(runtime->IsAotCompiler());
+ runtime->RecordResolveString(this, string_idx);
+ }
// TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
- Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+ runtime->GetHeap()->WriteBarrierEveryFieldOf(this);
+}
+
+inline void DexCache::ClearString(uint32_t string_idx) {
+ const uint32_t slot_idx = string_idx % NumStrings();
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ StringDexCacheType* slot = &GetStrings()[slot_idx];
+ // This is racy but should only be called from the transactional interpreter.
+ if (slot->load(std::memory_order_relaxed).string_index == string_idx) {
+ StringDexCachePair cleared(
+ nullptr,
+ StringDexCachePair::InvalidStringIndexForSlot(slot_idx));
+ slot->store(cleared, std::memory_order_relaxed);
+ }
}
inline Class* DexCache::GetResolvedType(uint32_t type_idx) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index caf00c2..7d4021f 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -56,12 +56,20 @@
// it's always non-null if the string id branch succeeds (except for the 0th string id).
// Set the initial state for the 0th entry to be {0,1} which is guaranteed to fail
// the lookup string id == stored id branch.
+ StringDexCachePair(String* string, uint32_t string_idx)
+ : string_pointer(string),
+ string_index(string_idx) {}
+ StringDexCachePair() = default;
+ StringDexCachePair(const StringDexCachePair&) = default;
+ StringDexCachePair& operator=(const StringDexCachePair&) = default;
+
static void Initialize(StringDexCacheType* strings) {
mirror::StringDexCachePair first_elem;
first_elem.string_pointer = GcRoot<String>(nullptr);
- first_elem.string_index = 1;
+ first_elem.string_index = InvalidStringIndexForSlot(0);
strings[0].store(first_elem, std::memory_order_relaxed);
}
+
static GcRoot<String> LookupString(StringDexCacheType* dex_cache,
uint32_t string_idx,
uint32_t cache_size) {
@@ -71,10 +79,15 @@
DCHECK(!index_string.string_pointer.IsNull());
return index_string.string_pointer;
}
+
+ static uint32_t InvalidStringIndexForSlot(uint32_t slot) {
+ // Since the cache size is a power of two, 0 will always map to slot 0.
+ // Use 1 for slot 0 and 0 for all other slots.
+ return (slot == 0) ? 1u : 0u;
+ }
};
using StringDexCacheType = std::atomic<StringDexCachePair>;
-
// C++ mirror of java.lang.DexCache.
class MANAGED DexCache FINAL : public Object {
public:
@@ -164,6 +177,10 @@
void SetResolvedString(uint32_t string_idx, mirror::String* resolved) ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Clear a string for a string_idx, used to undo string intern transactions to make sure
+ // the string isn't kept live.
+ void ClearString(uint32_t string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
Class* GetResolvedType(uint32_t type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
void SetResolvedType(uint32_t type_idx, Class* resolved) REQUIRES_SHARED(Locks::mutator_lock_);