Initialize dex cache while holding dex_lock
Fixes multiple threads calling RegisterDexFile occasionally getting
DCHECK failures due to the arrays not being null since the BSS ones
is per dex file.
Bug: 31369621
Test: test-art-host, no DCHECK failure during debug booting
Change-Id: I7b6e4cd03460dd1213eb4e044bdcf5f6103fd5f9
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 91e31d8..b3ff6c2 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -89,6 +89,7 @@
kDeoptimizedMethodsLock,
kClassLoaderClassesLock,
kDefaultMutexLevel,
+ kDexLock,
kMarkSweepLargeObjectLock,
kPinTableLock,
kJdwpObjectRegistryLock,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d67e111..bd04bfa 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -315,7 +315,7 @@
ClassLinker::ClassLinker(InternTable* intern_table)
// dex_lock_ is recursive as it may be used in stack dumping.
- : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel),
+ : dex_lock_("ClassLinker dex lock", kDexLock),
dex_cache_boot_image_class_lookup_required_(false),
failed_dex_cache_class_lookups_(0),
class_roots_(nullptr),
@@ -2107,28 +2107,22 @@
: static_cast<mirror::Array*>(mirror::IntArray::Alloc(self, length)));
}
-mirror::DexCache* ClassLinker::AllocDexCache(Thread* self,
- const DexFile& dex_file,
- LinearAlloc* linear_alloc) {
- StackHandleScope<6> hs(self);
- auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>(
- GetClassRoot(kJavaLangDexCache)->AllocObject(self))));
- if (dex_cache.Get() == nullptr) {
- self->AssertPendingOOMException();
- return nullptr;
- }
- auto location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str())));
- if (location.Get() == nullptr) {
- self->AssertPendingOOMException();
- return nullptr;
- }
+void ClassLinker::InitializeDexCache(Thread* self,
+ mirror::DexCache* dex_cache,
+ mirror::String* location,
+ const DexFile& dex_file,
+ LinearAlloc* linear_alloc) {
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
DexCacheArraysLayout layout(image_pointer_size_, &dex_file);
uint8_t* raw_arrays = nullptr;
- if (dex_file.GetOatDexFile() != nullptr &&
- dex_file.GetOatDexFile()->GetDexCacheArrays() != nullptr) {
- raw_arrays = dex_file.GetOatDexFile()->GetDexCacheArrays();
- } else if (dex_file.NumStringIds() != 0u || dex_file.NumTypeIds() != 0u ||
- dex_file.NumMethodIds() != 0u || dex_file.NumFieldIds() != 0u) {
+
+ const OatDexFile* const oat_dex = dex_file.GetOatDexFile();
+ if (oat_dex != nullptr && oat_dex->GetDexCacheArrays() != nullptr) {
+ raw_arrays = oat_dex->GetDexCacheArrays();
+ } else if (dex_file.NumStringIds() != 0u ||
+ dex_file.NumTypeIds() != 0u ||
+ dex_file.NumMethodIds() != 0u ||
+ dex_file.NumFieldIds() != 0u) {
// Zero-initialized.
raw_arrays = reinterpret_cast<uint8_t*>(linear_alloc->Alloc(self, layout.Size()));
}
@@ -2185,7 +2179,7 @@
CHECK(strings[i].load(std::memory_order_relaxed).object.IsNull());
}
for (size_t i = 0; i < dex_file.NumTypeIds(); ++i) {
- CHECK(types[i].Read<kWithoutReadBarrier>() == nullptr);
+ CHECK(types[i].IsNull());
}
for (size_t i = 0; i < dex_file.NumMethodIds(); ++i) {
CHECK(mirror::DexCache::GetElementPtrSize(methods, i, image_pointer_size_) == nullptr);
@@ -2205,7 +2199,7 @@
mirror::MethodTypeDexCachePair::Initialize(method_types);
}
dex_cache->Init(&dex_file,
- location.Get(),
+ location,
strings,
num_strings,
types,
@@ -2217,9 +2211,41 @@
method_types,
num_method_types,
image_pointer_size_);
+}
+
+mirror::DexCache* ClassLinker::AllocDexCache(mirror::String** out_location,
+ Thread* self,
+ const DexFile& dex_file) {
+ StackHandleScope<1> hs(self);
+ DCHECK(out_location != nullptr);
+ auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>(
+ GetClassRoot(kJavaLangDexCache)->AllocObject(self))));
+ if (dex_cache.Get() == nullptr) {
+ self->AssertPendingOOMException();
+ return nullptr;
+ }
+ mirror::String* location = intern_table_->InternStrong(dex_file.GetLocation().c_str());
+ if (location == nullptr) {
+ self->AssertPendingOOMException();
+ return nullptr;
+ }
+ *out_location = location;
return dex_cache.Get();
}
+mirror::DexCache* ClassLinker::AllocAndInitializeDexCache(Thread* self,
+ const DexFile& dex_file,
+ LinearAlloc* linear_alloc) {
+ mirror::String* location = nullptr;
+ mirror::DexCache* dex_cache = AllocDexCache(&location, self, dex_file);
+ if (dex_cache != nullptr) {
+ WriterMutexLock mu(self, dex_lock_);
+ DCHECK(location != nullptr);
+ InitializeDexCache(self, dex_cache, location, dex_file, linear_alloc);
+ }
+ return dex_cache;
+}
+
mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class,
uint32_t class_size) {
DCHECK_GE(class_size, sizeof(mirror::Class));
@@ -3292,7 +3318,7 @@
void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile& dex_file) {
StackHandleScope<1> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocAndInitializeDexCache(
self,
dex_file,
Runtime::Current()->GetLinearAlloc())));
@@ -3369,8 +3395,12 @@
// Don't alloc while holding the lock, since allocation may need to
// suspend all threads and another thread may need the dex_lock_ to
// get to a suspend point.
- StackHandleScope<1> hs(self);
- Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(AllocDexCache(self, dex_file, linear_alloc)));
+ StackHandleScope<2> hs(self);
+ mirror::String* location;
+ Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(AllocDexCache(/*out*/&location,
+ self,
+ dex_file)));
+ Handle<mirror::String> h_location(hs.NewHandle(location));
{
WriterMutexLock mu(self, dex_lock_);
mirror::DexCache* dex_cache = FindDexCacheLocked(self, dex_file, true);
@@ -3381,6 +3411,10 @@
self->AssertPendingOOMException();
return nullptr;
}
+ // Do InitializeDexCache while holding dex lock to make sure two threads don't call it at the
+ // same time with the same dex cache. Since the .bss is shared this can cause failing DCHECK
+ // that the arrays are null.
+ InitializeDexCache(self, h_dex_cache.Get(), h_location.Get(), dex_file, linear_alloc);
RegisterDexFileLocked(dex_file, h_dex_cache);
}
table->InsertStrongRoot(h_dex_cache.Get());
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index f69a576..d2eeaab 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -735,12 +735,29 @@
mirror::Class* AllocClass(Thread* self, uint32_t class_size)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
- mirror::DexCache* AllocDexCache(Thread* self,
- const DexFile& dex_file,
- LinearAlloc* linear_alloc)
+
+ mirror::DexCache* AllocDexCache(mirror::String** out_location,
+ Thread* self,
+ const DexFile& dex_file)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
+ // Used for tests and AppendToBootClassPath.
+ mirror::DexCache* AllocAndInitializeDexCache(Thread* self,
+ const DexFile& dex_file,
+ LinearAlloc* linear_alloc)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_)
+ REQUIRES(!Roles::uninterruptible_);
+
+ void InitializeDexCache(Thread* self,
+ mirror::DexCache* dex_cache,
+ mirror::String* location,
+ const DexFile& dex_file,
+ LinearAlloc* linear_alloc)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(dex_lock_);
+
mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 12301b8..e95ca21 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -44,9 +44,10 @@
StackHandleScope<1> hs(soa.Self());
ASSERT_TRUE(java_lang_dex_file_ != nullptr);
Handle<DexCache> dex_cache(
- hs.NewHandle(class_linker_->AllocDexCache(soa.Self(),
- *java_lang_dex_file_,
- Runtime::Current()->GetLinearAlloc())));
+ hs.NewHandle(class_linker_->AllocAndInitializeDexCache(
+ soa.Self(),
+ *java_lang_dex_file_,
+ Runtime::Current()->GetLinearAlloc())));
ASSERT_TRUE(dex_cache.Get() != nullptr);
EXPECT_TRUE(dex_cache->StaticStringSize() == dex_cache->NumStrings()
@@ -64,9 +65,10 @@
StackHandleScope<1> hs(soa.Self());
ASSERT_TRUE(java_lang_dex_file_ != nullptr);
Handle<DexCache> dex_cache(
- hs.NewHandle(class_linker_->AllocDexCache(soa.Self(),
- *java_lang_dex_file_,
- Runtime::Current()->GetLinearAlloc())));
+ hs.NewHandle(class_linker_->AllocAndInitializeDexCache(
+ soa.Self(),
+ *java_lang_dex_file_,
+ Runtime::Current()->GetLinearAlloc())));
EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
|| java_lang_dex_file_->NumProtoIds() == dex_cache->NumResolvedMethodTypes());