Merge "Disable com.android.runtime.host on darwin"
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index 7536c31..7253488 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -22,6 +22,7 @@
#include <unordered_set>
#include "base/bit_vector.h"
+#include "base/mutex.h"
#include "dex/dex_file.h"
#include "dex/invoke_type.h"
#include "dex/method_reference.h"
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 6108522..13c8684 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -26,6 +26,7 @@
#include "base/arena_object.h"
#include "base/array_ref.h"
#include "base/iteration_range.h"
+#include "base/mutex.h"
#include "base/quasi_atomic.h"
#include "base/stl_util.h"
#include "base/transform_array_ref.h"
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 410901e..b03ef60 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -33,6 +33,7 @@
"art_method.cc",
"backtrace_helper.cc",
"barrier.cc",
+ "base/locks.cc",
"base/mem_map_arena_pool.cc",
"base/mutex.cc",
"base/quasi_atomic.cc",
@@ -454,7 +455,7 @@
cmd: "$(location generate_operator_out) art/runtime $(in) > $(out)",
tools: ["generate_operator_out"],
srcs: [
- "base/mutex.h",
+ "base/locks.h",
"class_loader_context.h",
"class_status.h",
"debugger.h",
diff --git a/runtime/arch/context.h b/runtime/arch/context.h
index d067f66..5980b03 100644
--- a/runtime/arch/context.h
+++ b/runtime/arch/context.h
@@ -21,7 +21,6 @@
#include <stdint.h>
#include "base/macros.h"
-#include "base/mutex.h"
namespace art {
diff --git a/runtime/base/locks.cc b/runtime/base/locks.cc
new file mode 100644
index 0000000..cfc9f1d
--- /dev/null
+++ b/runtime/base/locks.cc
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2011 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 "locks.h"
+
+#include <errno.h>
+#include <sys/time.h>
+
+#include "android-base/logging.h"
+
+#include "base/atomic.h"
+#include "base/logging.h"
+#include "base/systrace.h"
+#include "base/time_utils.h"
+#include "base/value_object.h"
+#include "mutex-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace art {
+
+static Atomic<Locks::ClientCallback*> safe_to_call_abort_callback(nullptr);
+
+Mutex* Locks::abort_lock_ = nullptr;
+Mutex* Locks::alloc_tracker_lock_ = nullptr;
+Mutex* Locks::allocated_monitor_ids_lock_ = nullptr;
+Mutex* Locks::allocated_thread_ids_lock_ = nullptr;
+ReaderWriterMutex* Locks::breakpoint_lock_ = nullptr;
+ReaderWriterMutex* Locks::classlinker_classes_lock_ = nullptr;
+Mutex* Locks::custom_tls_lock_ = nullptr;
+Mutex* Locks::deoptimization_lock_ = nullptr;
+ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
+Mutex* Locks::instrument_entrypoints_lock_ = nullptr;
+Mutex* Locks::intern_table_lock_ = nullptr;
+Mutex* Locks::jni_function_table_lock_ = nullptr;
+Mutex* Locks::jni_libraries_lock_ = nullptr;
+Mutex* Locks::logging_lock_ = nullptr;
+Mutex* Locks::modify_ldt_lock_ = nullptr;
+MutatorMutex* Locks::mutator_lock_ = nullptr;
+Mutex* Locks::profiler_lock_ = nullptr;
+ReaderWriterMutex* Locks::verifier_deps_lock_ = nullptr;
+ReaderWriterMutex* Locks::oat_file_manager_lock_ = nullptr;
+Mutex* Locks::host_dlopen_handles_lock_ = nullptr;
+Mutex* Locks::reference_processor_lock_ = nullptr;
+Mutex* Locks::reference_queue_cleared_references_lock_ = nullptr;
+Mutex* Locks::reference_queue_finalizer_references_lock_ = nullptr;
+Mutex* Locks::reference_queue_phantom_references_lock_ = nullptr;
+Mutex* Locks::reference_queue_soft_references_lock_ = nullptr;
+Mutex* Locks::reference_queue_weak_references_lock_ = nullptr;
+Mutex* Locks::runtime_shutdown_lock_ = nullptr;
+Mutex* Locks::cha_lock_ = nullptr;
+Mutex* Locks::subtype_check_lock_ = nullptr;
+Mutex* Locks::thread_list_lock_ = nullptr;
+ConditionVariable* Locks::thread_exit_cond_ = nullptr;
+Mutex* Locks::thread_suspend_count_lock_ = nullptr;
+Mutex* Locks::trace_lock_ = nullptr;
+Mutex* Locks::unexpected_signal_lock_ = nullptr;
+Mutex* Locks::user_code_suspension_lock_ = nullptr;
+Uninterruptible Roles::uninterruptible_;
+ReaderWriterMutex* Locks::jni_globals_lock_ = nullptr;
+Mutex* Locks::jni_weak_globals_lock_ = nullptr;
+ReaderWriterMutex* Locks::dex_lock_ = nullptr;
+Mutex* Locks::native_debug_interface_lock_ = nullptr;
+std::vector<BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_;
+Atomic<const BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_guard_;
+
+// Wait for an amount of time that roughly increases in the argument i.
+// Spin for small arguments and yield/sleep for longer ones.
+static void BackOff(uint32_t i) {
+ static constexpr uint32_t kSpinMax = 10;
+ static constexpr uint32_t kYieldMax = 20;
+ if (i <= kSpinMax) {
+ // TODO: Esp. in very latency-sensitive cases, consider replacing this with an explicit
+ // test-and-test-and-set loop in the caller. Possibly skip entirely on a uniprocessor.
+ volatile uint32_t x = 0;
+ const uint32_t spin_count = 10 * i;
+ for (uint32_t spin = 0; spin < spin_count; ++spin) {
+ ++x; // Volatile; hence should not be optimized away.
+ }
+ // TODO: Consider adding x86 PAUSE and/or ARM YIELD here.
+ } else if (i <= kYieldMax) {
+ sched_yield();
+ } else {
+ NanoSleep(1000ull * (i - kYieldMax));
+ }
+}
+
+class Locks::ScopedExpectedMutexesOnWeakRefAccessLock final {
+ public:
+ explicit ScopedExpectedMutexesOnWeakRefAccessLock(const BaseMutex* mutex) : mutex_(mutex) {
+ for (uint32_t i = 0;
+ !Locks::expected_mutexes_on_weak_ref_access_guard_.CompareAndSetWeakAcquire(nullptr,
+ mutex);
+ ++i) {
+ BackOff(i);
+ }
+ }
+
+ ~ScopedExpectedMutexesOnWeakRefAccessLock() {
+ DCHECK_EQ(Locks::expected_mutexes_on_weak_ref_access_guard_.load(std::memory_order_relaxed),
+ mutex_);
+ Locks::expected_mutexes_on_weak_ref_access_guard_.store(nullptr, std::memory_order_release);
+ }
+
+ private:
+ const BaseMutex* const mutex_;
+};
+
+void Locks::Init() {
+ if (logging_lock_ != nullptr) {
+ // Already initialized.
+ if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) {
+ DCHECK(modify_ldt_lock_ != nullptr);
+ } else {
+ DCHECK(modify_ldt_lock_ == nullptr);
+ }
+ DCHECK(abort_lock_ != nullptr);
+ DCHECK(alloc_tracker_lock_ != nullptr);
+ DCHECK(allocated_monitor_ids_lock_ != nullptr);
+ DCHECK(allocated_thread_ids_lock_ != nullptr);
+ DCHECK(breakpoint_lock_ != nullptr);
+ DCHECK(classlinker_classes_lock_ != nullptr);
+ DCHECK(custom_tls_lock_ != nullptr);
+ DCHECK(deoptimization_lock_ != nullptr);
+ DCHECK(heap_bitmap_lock_ != nullptr);
+ DCHECK(oat_file_manager_lock_ != nullptr);
+ DCHECK(verifier_deps_lock_ != nullptr);
+ DCHECK(host_dlopen_handles_lock_ != nullptr);
+ DCHECK(intern_table_lock_ != nullptr);
+ DCHECK(jni_function_table_lock_ != nullptr);
+ DCHECK(jni_libraries_lock_ != nullptr);
+ DCHECK(logging_lock_ != nullptr);
+ DCHECK(mutator_lock_ != nullptr);
+ DCHECK(profiler_lock_ != nullptr);
+ DCHECK(cha_lock_ != nullptr);
+ DCHECK(subtype_check_lock_ != nullptr);
+ DCHECK(thread_list_lock_ != nullptr);
+ DCHECK(thread_suspend_count_lock_ != nullptr);
+ DCHECK(trace_lock_ != nullptr);
+ DCHECK(unexpected_signal_lock_ != nullptr);
+ DCHECK(user_code_suspension_lock_ != nullptr);
+ DCHECK(dex_lock_ != nullptr);
+ DCHECK(native_debug_interface_lock_ != nullptr);
+ } else {
+ // Create global locks in level order from highest lock level to lowest.
+ LockLevel current_lock_level = kInstrumentEntrypointsLock;
+ DCHECK(instrument_entrypoints_lock_ == nullptr);
+ instrument_entrypoints_lock_ = new Mutex("instrument entrypoint lock", current_lock_level);
+
+ #define UPDATE_CURRENT_LOCK_LEVEL(new_level) \
+ if ((new_level) >= current_lock_level) { \
+ /* Do not use CHECKs or FATAL here, abort_lock_ is not setup yet. */ \
+ fprintf(stderr, "New local level %d is not less than current level %d\n", \
+ new_level, current_lock_level); \
+ exit(1); \
+ } \
+ current_lock_level = new_level;
+
+ UPDATE_CURRENT_LOCK_LEVEL(kUserCodeSuspensionLock);
+ DCHECK(user_code_suspension_lock_ == nullptr);
+ user_code_suspension_lock_ = new Mutex("user code suspension lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kMutatorLock);
+ DCHECK(mutator_lock_ == nullptr);
+ mutator_lock_ = new MutatorMutex("mutator lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kHeapBitmapLock);
+ DCHECK(heap_bitmap_lock_ == nullptr);
+ heap_bitmap_lock_ = new ReaderWriterMutex("heap bitmap lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kTraceLock);
+ DCHECK(trace_lock_ == nullptr);
+ trace_lock_ = new Mutex("trace lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kRuntimeShutdownLock);
+ DCHECK(runtime_shutdown_lock_ == nullptr);
+ runtime_shutdown_lock_ = new Mutex("runtime shutdown lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kProfilerLock);
+ DCHECK(profiler_lock_ == nullptr);
+ profiler_lock_ = new Mutex("profiler lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kDeoptimizationLock);
+ DCHECK(deoptimization_lock_ == nullptr);
+ deoptimization_lock_ = new Mutex("Deoptimization lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kAllocTrackerLock);
+ DCHECK(alloc_tracker_lock_ == nullptr);
+ alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock);
+ DCHECK(thread_list_lock_ == nullptr);
+ thread_list_lock_ = new Mutex("thread list lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kJniLoadLibraryLock);
+ DCHECK(jni_libraries_lock_ == nullptr);
+ jni_libraries_lock_ = new Mutex("JNI shared libraries map lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kBreakpointLock);
+ DCHECK(breakpoint_lock_ == nullptr);
+ breakpoint_lock_ = new ReaderWriterMutex("breakpoint lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kSubtypeCheckLock);
+ DCHECK(subtype_check_lock_ == nullptr);
+ subtype_check_lock_ = new Mutex("SubtypeCheck lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kClassLinkerClassesLock);
+ DCHECK(classlinker_classes_lock_ == nullptr);
+ classlinker_classes_lock_ = new ReaderWriterMutex("ClassLinker classes lock",
+ current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kMonitorPoolLock);
+ DCHECK(allocated_monitor_ids_lock_ == nullptr);
+ allocated_monitor_ids_lock_ = new Mutex("allocated monitor ids lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kAllocatedThreadIdsLock);
+ DCHECK(allocated_thread_ids_lock_ == nullptr);
+ allocated_thread_ids_lock_ = new Mutex("allocated thread ids lock", current_lock_level);
+
+ if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) {
+ UPDATE_CURRENT_LOCK_LEVEL(kModifyLdtLock);
+ DCHECK(modify_ldt_lock_ == nullptr);
+ modify_ldt_lock_ = new Mutex("modify_ldt lock", current_lock_level);
+ }
+
+ UPDATE_CURRENT_LOCK_LEVEL(kDexLock);
+ DCHECK(dex_lock_ == nullptr);
+ dex_lock_ = new ReaderWriterMutex("ClassLinker dex lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kOatFileManagerLock);
+ DCHECK(oat_file_manager_lock_ == nullptr);
+ oat_file_manager_lock_ = new ReaderWriterMutex("OatFile manager lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kVerifierDepsLock);
+ DCHECK(verifier_deps_lock_ == nullptr);
+ verifier_deps_lock_ = new ReaderWriterMutex("verifier deps lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kHostDlOpenHandlesLock);
+ DCHECK(host_dlopen_handles_lock_ == nullptr);
+ host_dlopen_handles_lock_ = new Mutex("host dlopen handles lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kInternTableLock);
+ DCHECK(intern_table_lock_ == nullptr);
+ intern_table_lock_ = new Mutex("InternTable lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kReferenceProcessorLock);
+ DCHECK(reference_processor_lock_ == nullptr);
+ reference_processor_lock_ = new Mutex("ReferenceProcessor lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueClearedReferencesLock);
+ DCHECK(reference_queue_cleared_references_lock_ == nullptr);
+ reference_queue_cleared_references_lock_ = new Mutex("ReferenceQueue cleared references lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueWeakReferencesLock);
+ DCHECK(reference_queue_weak_references_lock_ == nullptr);
+ reference_queue_weak_references_lock_ = new Mutex("ReferenceQueue cleared references lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueFinalizerReferencesLock);
+ DCHECK(reference_queue_finalizer_references_lock_ == nullptr);
+ reference_queue_finalizer_references_lock_ = new Mutex("ReferenceQueue finalizer references lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueuePhantomReferencesLock);
+ DCHECK(reference_queue_phantom_references_lock_ == nullptr);
+ reference_queue_phantom_references_lock_ = new Mutex("ReferenceQueue phantom references lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueSoftReferencesLock);
+ DCHECK(reference_queue_soft_references_lock_ == nullptr);
+ reference_queue_soft_references_lock_ = new Mutex("ReferenceQueue soft references lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kJniGlobalsLock);
+ DCHECK(jni_globals_lock_ == nullptr);
+ jni_globals_lock_ =
+ new ReaderWriterMutex("JNI global reference table lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kJniWeakGlobalsLock);
+ DCHECK(jni_weak_globals_lock_ == nullptr);
+ jni_weak_globals_lock_ = new Mutex("JNI weak global reference table lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kJniFunctionTableLock);
+ DCHECK(jni_function_table_lock_ == nullptr);
+ jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kCustomTlsLock);
+ DCHECK(custom_tls_lock_ == nullptr);
+ custom_tls_lock_ = new Mutex("Thread::custom_tls_ lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kCHALock);
+ DCHECK(cha_lock_ == nullptr);
+ cha_lock_ = new Mutex("CHA lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kNativeDebugInterfaceLock);
+ DCHECK(native_debug_interface_lock_ == nullptr);
+ native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
+ DCHECK(abort_lock_ == nullptr);
+ abort_lock_ = new Mutex("abort lock", current_lock_level, true);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kThreadSuspendCountLock);
+ DCHECK(thread_suspend_count_lock_ == nullptr);
+ thread_suspend_count_lock_ = new Mutex("thread suspend count lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kUnexpectedSignalLock);
+ DCHECK(unexpected_signal_lock_ == nullptr);
+ unexpected_signal_lock_ = new Mutex("unexpected signal lock", current_lock_level, true);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kLoggingLock);
+ DCHECK(logging_lock_ == nullptr);
+ logging_lock_ = new Mutex("logging lock", current_lock_level, true);
+
+ #undef UPDATE_CURRENT_LOCK_LEVEL
+
+ // List of mutexes that we may hold when accessing a weak ref.
+ AddToExpectedMutexesOnWeakRefAccess(dex_lock_, /*need_lock=*/ false);
+ AddToExpectedMutexesOnWeakRefAccess(classlinker_classes_lock_, /*need_lock=*/ false);
+ AddToExpectedMutexesOnWeakRefAccess(jni_libraries_lock_, /*need_lock=*/ false);
+
+ InitConditions();
+ }
+}
+
+void Locks::InitConditions() {
+ thread_exit_cond_ = new ConditionVariable("thread exit condition variable", *thread_list_lock_);
+}
+
+void Locks::SetClientCallback(ClientCallback* safe_to_call_abort_cb) {
+ safe_to_call_abort_callback.store(safe_to_call_abort_cb, std::memory_order_release);
+}
+
+// Helper to allow checking shutdown while ignoring locking requirements.
+bool Locks::IsSafeToCallAbortRacy() {
+ Locks::ClientCallback* safe_to_call_abort_cb =
+ safe_to_call_abort_callback.load(std::memory_order_acquire);
+ return safe_to_call_abort_cb != nullptr && safe_to_call_abort_cb();
+}
+
+void Locks::AddToExpectedMutexesOnWeakRefAccess(BaseMutex* mutex, bool need_lock) {
+ if (need_lock) {
+ ScopedExpectedMutexesOnWeakRefAccessLock mu(mutex);
+ mutex->SetShouldRespondToEmptyCheckpointRequest(true);
+ expected_mutexes_on_weak_ref_access_.push_back(mutex);
+ } else {
+ mutex->SetShouldRespondToEmptyCheckpointRequest(true);
+ expected_mutexes_on_weak_ref_access_.push_back(mutex);
+ }
+}
+
+void Locks::RemoveFromExpectedMutexesOnWeakRefAccess(BaseMutex* mutex, bool need_lock) {
+ if (need_lock) {
+ ScopedExpectedMutexesOnWeakRefAccessLock mu(mutex);
+ mutex->SetShouldRespondToEmptyCheckpointRequest(false);
+ std::vector<BaseMutex*>& list = expected_mutexes_on_weak_ref_access_;
+ auto it = std::find(list.begin(), list.end(), mutex);
+ DCHECK(it != list.end());
+ list.erase(it);
+ } else {
+ mutex->SetShouldRespondToEmptyCheckpointRequest(false);
+ std::vector<BaseMutex*>& list = expected_mutexes_on_weak_ref_access_;
+ auto it = std::find(list.begin(), list.end(), mutex);
+ DCHECK(it != list.end());
+ list.erase(it);
+ }
+}
+
+bool Locks::IsExpectedOnWeakRefAccess(BaseMutex* mutex) {
+ ScopedExpectedMutexesOnWeakRefAccessLock mu(mutex);
+ std::vector<BaseMutex*>& list = expected_mutexes_on_weak_ref_access_;
+ return std::find(list.begin(), list.end(), mutex) != list.end();
+}
+
+} // namespace art
diff --git a/runtime/base/locks.h b/runtime/base/locks.h
new file mode 100644
index 0000000..8cbe372
--- /dev/null
+++ b/runtime/base/locks.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2011 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_BASE_LOCKS_H_
+#define ART_RUNTIME_BASE_LOCKS_H_
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <vector>
+
+#include "base/atomic.h"
+#include "base/macros.h"
+
+namespace art {
+
+class BaseMutex;
+class ConditionVariable;
+class SHARED_LOCKABLE ReaderWriterMutex;
+class SHARED_LOCKABLE MutatorMutex;
+class LOCKABLE Mutex;
+class Thread;
+
+// LockLevel is used to impose a lock hierarchy [1] where acquisition of a Mutex at a higher or
+// equal level to a lock a thread holds is invalid. The lock hierarchy achieves a cycle free
+// partial ordering and thereby cause deadlock situations to fail checks.
+//
+// [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163
+enum LockLevel : uint8_t {
+ kLoggingLock = 0,
+ kSwapMutexesLock,
+ kUnexpectedSignalLock,
+ kThreadSuspendCountLock,
+ kAbortLock,
+ kNativeDebugInterfaceLock,
+ kSignalHandlingLock,
+ // A generic lock level for mutexs that should not allow any additional mutexes to be gained after
+ // acquiring it.
+ kGenericBottomLock,
+ // Tracks the second acquisition at the same lock level for kThreadWaitLock. This is an exception
+ // to the normal lock ordering, used to implement Monitor::Wait - while holding one kThreadWait
+ // level lock, it is permitted to acquire a second one - with internal safeguards to ensure that
+ // the second lock acquisition does not result in deadlock. This is implemented in the lock
+ // order by treating the second acquisition of a kThreadWaitLock as a kThreadWaitWakeLock
+ // acquisition. Thus, acquiring kThreadWaitWakeLock requires holding kThreadWaitLock. This entry
+ // is here near the bottom of the hierarchy because other locks should not be
+ // acquired while it is held. kThreadWaitLock cannot be moved here because GC
+ // activity acquires locks while holding the wait lock.
+ kThreadWaitWakeLock,
+ kJdwpAdbStateLock,
+ kJdwpSocketLock,
+ kRegionSpaceRegionLock,
+ kMarkSweepMarkStackLock,
+ // Can be held while GC related work is done, and thus must be above kMarkSweepMarkStackLock
+ kThreadWaitLock,
+ kCHALock,
+ kJitCodeCacheLock,
+ kRosAllocGlobalLock,
+ kRosAllocBracketLock,
+ kRosAllocBulkFreeLock,
+ kTaggingLockLevel,
+ kTransactionLogLock,
+ kCustomTlsLock,
+ kJniFunctionTableLock,
+ kJniWeakGlobalsLock,
+ kJniGlobalsLock,
+ kReferenceQueueSoftReferencesLock,
+ kReferenceQueuePhantomReferencesLock,
+ kReferenceQueueFinalizerReferencesLock,
+ kReferenceQueueWeakReferencesLock,
+ kReferenceQueueClearedReferencesLock,
+ kReferenceProcessorLock,
+ kJitDebugInterfaceLock,
+ kAllocSpaceLock,
+ kBumpPointerSpaceBlockLock,
+ kArenaPoolLock,
+ kInternTableLock,
+ kOatFileSecondaryLookupLock,
+ kHostDlOpenHandlesLock,
+ kVerifierDepsLock,
+ kOatFileManagerLock,
+ kTracingUniqueMethodsLock,
+ kTracingStreamingLock,
+ kClassLoaderClassesLock,
+ kDefaultMutexLevel,
+ kDexLock,
+ kMarkSweepLargeObjectLock,
+ kJdwpObjectRegistryLock,
+ kModifyLdtLock,
+ kAllocatedThreadIdsLock,
+ kMonitorPoolLock,
+ kClassLinkerClassesLock, // TODO rename.
+ kDexToDexCompilerLock,
+ kSubtypeCheckLock,
+ kBreakpointLock,
+ kMonitorLock,
+ kMonitorListLock,
+ kJniLoadLibraryLock,
+ kThreadListLock,
+ kAllocTrackerLock,
+ kDeoptimizationLock,
+ kProfilerLock,
+ kJdwpShutdownLock,
+ kJdwpEventListLock,
+ kJdwpAttachLock,
+ kJdwpStartLock,
+ kRuntimeShutdownLock,
+ kTraceLock,
+ kHeapBitmapLock,
+ kMutatorLock,
+ kUserCodeSuspensionLock,
+ kInstrumentEntrypointsLock,
+ kZygoteCreationLock,
+
+ // The highest valid lock level. Use this if there is code that should only be called with no
+ // other locks held. Since this is the highest lock level we also allow it to be held even if the
+ // runtime or current thread is not fully set-up yet (for example during thread attach). Note that
+ // this lock also has special behavior around the mutator_lock_. Since the mutator_lock_ is not
+ // really a 'real' lock we allow this to be locked when the mutator_lock_ is held exclusive.
+ // Furthermore, the mutator_lock_ may not be acquired in any form when a lock of this level is
+ // held. Since the mutator_lock_ being held strong means that all other threads are suspended this
+ // will prevent deadlocks while still allowing this lock level to function as a "highest" level.
+ kTopLockLevel,
+
+ kLockLevelCount // Must come last.
+};
+std::ostream& operator<<(std::ostream& os, const LockLevel& rhs);
+
+// For StartNoThreadSuspension and EndNoThreadSuspension.
+class CAPABILITY("role") Role {
+ public:
+ void Acquire() ACQUIRE() {}
+ void Release() RELEASE() {}
+ const Role& operator!() const { return *this; }
+};
+
+class Uninterruptible : public Role {
+};
+
+// Global mutexes corresponding to the levels above.
+class Locks {
+ public:
+ static void Init();
+ static void InitConditions() NO_THREAD_SAFETY_ANALYSIS; // Condition variables.
+
+ // Destroying various lock types can emit errors that vary depending upon
+ // whether the client (art::Runtime) is currently active. Allow the client
+ // to set a callback that is used to check when it is acceptable to call
+ // Abort. The default behavior is that the client *is not* able to call
+ // Abort if no callback is established.
+ using ClientCallback = bool();
+ static void SetClientCallback(ClientCallback* is_safe_to_call_abort_cb) NO_THREAD_SAFETY_ANALYSIS;
+ // Checks for whether it is safe to call Abort() without using locks.
+ static bool IsSafeToCallAbortRacy() NO_THREAD_SAFETY_ANALYSIS;
+
+ // Add a mutex to expected_mutexes_on_weak_ref_access_.
+ static void AddToExpectedMutexesOnWeakRefAccess(BaseMutex* mutex, bool need_lock = true);
+ // Remove a mutex from expected_mutexes_on_weak_ref_access_.
+ static void RemoveFromExpectedMutexesOnWeakRefAccess(BaseMutex* mutex, bool need_lock = true);
+ // Check if the given mutex is in expected_mutexes_on_weak_ref_access_.
+ static bool IsExpectedOnWeakRefAccess(BaseMutex* mutex);
+
+ // Guards allocation entrypoint instrumenting.
+ static Mutex* instrument_entrypoints_lock_;
+
+ // Guards code that deals with user-code suspension. This mutex must be held when suspending or
+ // resuming threads with SuspendReason::kForUserCode. It may be held by a suspended thread, but
+ // only if the suspension is not due to SuspendReason::kForUserCode.
+ static Mutex* user_code_suspension_lock_ ACQUIRED_AFTER(instrument_entrypoints_lock_);
+
+ // A barrier is used to synchronize the GC/Debugger thread with mutator threads. When GC/Debugger
+ // thread wants to suspend all mutator threads, it needs to wait for all mutator threads to pass
+ // a barrier. Threads that are already suspended will get their barrier passed by the GC/Debugger
+ // thread; threads in the runnable state will pass the barrier when they transit to the suspended
+ // state. GC/Debugger thread will be woken up when all mutator threads are suspended.
+ //
+ // Thread suspension:
+ // mutator thread | GC/Debugger
+ // .. running .. | .. running ..
+ // .. running .. | Request thread suspension by:
+ // .. running .. | - acquiring thread_suspend_count_lock_
+ // .. running .. | - incrementing Thread::suspend_count_ on
+ // .. running .. | all mutator threads
+ // .. running .. | - releasing thread_suspend_count_lock_
+ // .. running .. | Block wait for all threads to pass a barrier
+ // Poll Thread::suspend_count_ and enter full | .. blocked ..
+ // suspend code. | .. blocked ..
+ // Change state to kSuspended (pass the barrier) | Wake up when all threads pass the barrier
+ // x: Acquire thread_suspend_count_lock_ | .. running ..
+ // while Thread::suspend_count_ > 0 | .. running ..
+ // - wait on Thread::resume_cond_ | .. running ..
+ // (releases thread_suspend_count_lock_) | .. running ..
+ // .. waiting .. | Request thread resumption by:
+ // .. waiting .. | - acquiring thread_suspend_count_lock_
+ // .. waiting .. | - decrementing Thread::suspend_count_ on
+ // .. waiting .. | all mutator threads
+ // .. waiting .. | - notifying on Thread::resume_cond_
+ // - re-acquire thread_suspend_count_lock_ | - releasing thread_suspend_count_lock_
+ // Release thread_suspend_count_lock_ | .. running ..
+ // Change to kRunnable | .. running ..
+ // - this uses a CAS operation to ensure the | .. running ..
+ // suspend request flag isn't raised as the | .. running ..
+ // state is changed | .. running ..
+ // - if the CAS operation fails then goto x | .. running ..
+ // .. running .. | .. running ..
+ static MutatorMutex* mutator_lock_ ACQUIRED_AFTER(user_code_suspension_lock_);
+
+ // Allow reader-writer mutual exclusion on the mark and live bitmaps of the heap.
+ static ReaderWriterMutex* heap_bitmap_lock_ ACQUIRED_AFTER(mutator_lock_);
+
+ // Guards shutdown of the runtime.
+ static Mutex* runtime_shutdown_lock_ ACQUIRED_AFTER(heap_bitmap_lock_);
+
+ // Guards background profiler global state.
+ static Mutex* profiler_lock_ ACQUIRED_AFTER(runtime_shutdown_lock_);
+
+ // Guards trace (ie traceview) requests.
+ static Mutex* trace_lock_ ACQUIRED_AFTER(profiler_lock_);
+
+ // Guards debugger recent allocation records.
+ static Mutex* alloc_tracker_lock_ ACQUIRED_AFTER(trace_lock_);
+
+ // Guards updates to instrumentation to ensure mutual exclusion of
+ // events like deoptimization requests.
+ // TODO: improve name, perhaps instrumentation_update_lock_.
+ static Mutex* deoptimization_lock_ ACQUIRED_AFTER(alloc_tracker_lock_);
+
+ // Guard the update of the SubtypeCheck data stores in each Class::status_ field.
+ // This lock is used in SubtypeCheck methods which are the interface for
+ // any SubtypeCheck-mutating methods.
+ // In Class::IsSubClass, the lock is not required since it does not update the SubtypeCheck data.
+ static Mutex* subtype_check_lock_ ACQUIRED_AFTER(deoptimization_lock_);
+
+ // The thread_list_lock_ guards ThreadList::list_. It is also commonly held to stop threads
+ // attaching and detaching.
+ static Mutex* thread_list_lock_ ACQUIRED_AFTER(subtype_check_lock_);
+
+ // Signaled when threads terminate. Used to determine when all non-daemons have terminated.
+ static ConditionVariable* thread_exit_cond_ GUARDED_BY(Locks::thread_list_lock_);
+
+ // Guards maintaining loading library data structures.
+ static Mutex* jni_libraries_lock_ ACQUIRED_AFTER(thread_list_lock_);
+
+ // Guards breakpoints.
+ static ReaderWriterMutex* breakpoint_lock_ ACQUIRED_AFTER(jni_libraries_lock_);
+
+ // Guards lists of classes within the class linker.
+ static ReaderWriterMutex* classlinker_classes_lock_ ACQUIRED_AFTER(breakpoint_lock_);
+
+ // When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code
+ // doesn't try to hold a higher level Mutex.
+ #define DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(art::Locks::classlinker_classes_lock_)
+
+ static Mutex* allocated_monitor_ids_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
+
+ // Guard the allocation/deallocation of thread ids.
+ static Mutex* allocated_thread_ids_lock_ ACQUIRED_AFTER(allocated_monitor_ids_lock_);
+
+ // Guards modification of the LDT on x86.
+ static Mutex* modify_ldt_lock_ ACQUIRED_AFTER(allocated_thread_ids_lock_);
+
+ static ReaderWriterMutex* dex_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
+
+ // Guards opened oat files in OatFileManager.
+ static ReaderWriterMutex* oat_file_manager_lock_ ACQUIRED_AFTER(dex_lock_);
+
+ // Guards extra string entries for VerifierDeps.
+ static ReaderWriterMutex* verifier_deps_lock_ ACQUIRED_AFTER(oat_file_manager_lock_);
+
+ // Guards dlopen_handles_ in DlOpenOatFile.
+ static Mutex* host_dlopen_handles_lock_ ACQUIRED_AFTER(verifier_deps_lock_);
+
+ // Guards intern table.
+ static Mutex* intern_table_lock_ ACQUIRED_AFTER(host_dlopen_handles_lock_);
+
+ // Guards reference processor.
+ static Mutex* reference_processor_lock_ ACQUIRED_AFTER(intern_table_lock_);
+
+ // Guards cleared references queue.
+ static Mutex* reference_queue_cleared_references_lock_ ACQUIRED_AFTER(reference_processor_lock_);
+
+ // Guards weak references queue.
+ static Mutex* reference_queue_weak_references_lock_ ACQUIRED_AFTER(reference_queue_cleared_references_lock_);
+
+ // Guards finalizer references queue.
+ static Mutex* reference_queue_finalizer_references_lock_ ACQUIRED_AFTER(reference_queue_weak_references_lock_);
+
+ // Guards phantom references queue.
+ static Mutex* reference_queue_phantom_references_lock_ ACQUIRED_AFTER(reference_queue_finalizer_references_lock_);
+
+ // Guards soft references queue.
+ static Mutex* reference_queue_soft_references_lock_ ACQUIRED_AFTER(reference_queue_phantom_references_lock_);
+
+ // Guard accesses to the JNI Global Reference table.
+ static ReaderWriterMutex* jni_globals_lock_ ACQUIRED_AFTER(reference_queue_soft_references_lock_);
+
+ // Guard accesses to the JNI Weak Global Reference table.
+ static Mutex* jni_weak_globals_lock_ ACQUIRED_AFTER(jni_globals_lock_);
+
+ // Guard accesses to the JNI function table override.
+ static Mutex* jni_function_table_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
+
+ // Guard accesses to the Thread::custom_tls_. We use this to allow the TLS of other threads to be
+ // read (the reader must hold the ThreadListLock or have some other way of ensuring the thread
+ // will not die in that case though). This is useful for (eg) the implementation of
+ // GetThreadLocalStorage.
+ static Mutex* custom_tls_lock_ ACQUIRED_AFTER(jni_function_table_lock_);
+
+ // Guards Class Hierarchy Analysis (CHA).
+ static Mutex* cha_lock_ ACQUIRED_AFTER(custom_tls_lock_);
+
+ // When declaring any Mutex add BOTTOM_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code
+ // doesn't try to acquire a higher level Mutex. NB Due to the way the annotalysis works this
+ // actually only encodes the mutex being below jni_function_table_lock_ although having
+ // kGenericBottomLock level is lower than this.
+ #define BOTTOM_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(art::Locks::cha_lock_)
+
+ // Have an exclusive aborting thread.
+ static Mutex* abort_lock_ ACQUIRED_AFTER(custom_tls_lock_);
+
+ // Allow mutual exclusion when manipulating Thread::suspend_count_.
+ // TODO: Does the trade-off of a per-thread lock make sense?
+ static Mutex* thread_suspend_count_lock_ ACQUIRED_AFTER(abort_lock_);
+
+ // One unexpected signal at a time lock.
+ static Mutex* unexpected_signal_lock_ ACQUIRED_AFTER(thread_suspend_count_lock_);
+
+ // Guards the magic global variables used by native tools (e.g. libunwind).
+ static Mutex* native_debug_interface_lock_ ACQUIRED_AFTER(unexpected_signal_lock_);
+
+ // Have an exclusive logging thread.
+ static Mutex* logging_lock_ ACQUIRED_AFTER(native_debug_interface_lock_);
+
+ // List of mutexes that we expect a thread may hold when accessing weak refs. This is used to
+ // avoid a deadlock in the empty checkpoint while weak ref access is disabled (b/34964016). If we
+ // encounter an unexpected mutex on accessing weak refs,
+ // Thread::CheckEmptyCheckpointFromWeakRefAccess will detect it.
+ static std::vector<BaseMutex*> expected_mutexes_on_weak_ref_access_;
+ static Atomic<const BaseMutex*> expected_mutexes_on_weak_ref_access_guard_;
+ class ScopedExpectedMutexesOnWeakRefAccessLock;
+};
+
+class Roles {
+ public:
+ // Uninterruptible means that the thread may not become suspended.
+ static Uninterruptible uninterruptible_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_LOCKS_H_
diff --git a/runtime/base/mutator_locked_dumpable.h b/runtime/base/mutator_locked_dumpable.h
index cf2199c..afbd732 100644
--- a/runtime/base/mutator_locked_dumpable.h
+++ b/runtime/base/mutator_locked_dumpable.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_
#define ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "thread-current-inl.h"
namespace art {
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index ca2ed80..5a52818 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -34,51 +34,6 @@
using android::base::StringPrintf;
-static Atomic<Locks::ClientCallback*> safe_to_call_abort_callback(nullptr);
-
-Mutex* Locks::abort_lock_ = nullptr;
-Mutex* Locks::alloc_tracker_lock_ = nullptr;
-Mutex* Locks::allocated_monitor_ids_lock_ = nullptr;
-Mutex* Locks::allocated_thread_ids_lock_ = nullptr;
-ReaderWriterMutex* Locks::breakpoint_lock_ = nullptr;
-ReaderWriterMutex* Locks::classlinker_classes_lock_ = nullptr;
-Mutex* Locks::custom_tls_lock_ = nullptr;
-Mutex* Locks::deoptimization_lock_ = nullptr;
-ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
-Mutex* Locks::instrument_entrypoints_lock_ = nullptr;
-Mutex* Locks::intern_table_lock_ = nullptr;
-Mutex* Locks::jni_function_table_lock_ = nullptr;
-Mutex* Locks::jni_libraries_lock_ = nullptr;
-Mutex* Locks::logging_lock_ = nullptr;
-Mutex* Locks::modify_ldt_lock_ = nullptr;
-MutatorMutex* Locks::mutator_lock_ = nullptr;
-Mutex* Locks::profiler_lock_ = nullptr;
-ReaderWriterMutex* Locks::verifier_deps_lock_ = nullptr;
-ReaderWriterMutex* Locks::oat_file_manager_lock_ = nullptr;
-Mutex* Locks::host_dlopen_handles_lock_ = nullptr;
-Mutex* Locks::reference_processor_lock_ = nullptr;
-Mutex* Locks::reference_queue_cleared_references_lock_ = nullptr;
-Mutex* Locks::reference_queue_finalizer_references_lock_ = nullptr;
-Mutex* Locks::reference_queue_phantom_references_lock_ = nullptr;
-Mutex* Locks::reference_queue_soft_references_lock_ = nullptr;
-Mutex* Locks::reference_queue_weak_references_lock_ = nullptr;
-Mutex* Locks::runtime_shutdown_lock_ = nullptr;
-Mutex* Locks::cha_lock_ = nullptr;
-Mutex* Locks::subtype_check_lock_ = nullptr;
-Mutex* Locks::thread_list_lock_ = nullptr;
-ConditionVariable* Locks::thread_exit_cond_ = nullptr;
-Mutex* Locks::thread_suspend_count_lock_ = nullptr;
-Mutex* Locks::trace_lock_ = nullptr;
-Mutex* Locks::unexpected_signal_lock_ = nullptr;
-Mutex* Locks::user_code_suspension_lock_ = nullptr;
-Uninterruptible Roles::uninterruptible_;
-ReaderWriterMutex* Locks::jni_globals_lock_ = nullptr;
-Mutex* Locks::jni_weak_globals_lock_ = nullptr;
-ReaderWriterMutex* Locks::dex_lock_ = nullptr;
-Mutex* Locks::native_debug_interface_lock_ = nullptr;
-std::vector<BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_;
-Atomic<const BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_guard_;
-
struct AllMutexData {
// A guard for all_mutexes_ that's not a mutex (Mutexes must CAS to acquire and busy wait).
Atomic<const BaseMutex*> all_mutexes_guard;
@@ -144,27 +99,6 @@
const BaseMutex* const mutex_;
};
-class Locks::ScopedExpectedMutexesOnWeakRefAccessLock final {
- public:
- explicit ScopedExpectedMutexesOnWeakRefAccessLock(const BaseMutex* mutex) : mutex_(mutex) {
- for (uint32_t i = 0;
- !Locks::expected_mutexes_on_weak_ref_access_guard_.CompareAndSetWeakAcquire(nullptr,
- mutex);
- ++i) {
- BackOff(i);
- }
- }
-
- ~ScopedExpectedMutexesOnWeakRefAccessLock() {
- DCHECK_EQ(Locks::expected_mutexes_on_weak_ref_access_guard_.load(std::memory_order_relaxed),
- mutex_);
- Locks::expected_mutexes_on_weak_ref_access_guard_.store(nullptr, std::memory_order_release);
- }
-
- private:
- const BaseMutex* const mutex_;
-};
-
// Scoped class that generates events at the beginning and end of lock contention.
class ScopedContentionRecorder final : public ValueObject {
public:
@@ -1042,266 +976,4 @@
return timed_out;
}
-void Locks::Init() {
- if (logging_lock_ != nullptr) {
- // Already initialized.
- if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) {
- DCHECK(modify_ldt_lock_ != nullptr);
- } else {
- DCHECK(modify_ldt_lock_ == nullptr);
- }
- DCHECK(abort_lock_ != nullptr);
- DCHECK(alloc_tracker_lock_ != nullptr);
- DCHECK(allocated_monitor_ids_lock_ != nullptr);
- DCHECK(allocated_thread_ids_lock_ != nullptr);
- DCHECK(breakpoint_lock_ != nullptr);
- DCHECK(classlinker_classes_lock_ != nullptr);
- DCHECK(custom_tls_lock_ != nullptr);
- DCHECK(deoptimization_lock_ != nullptr);
- DCHECK(heap_bitmap_lock_ != nullptr);
- DCHECK(oat_file_manager_lock_ != nullptr);
- DCHECK(verifier_deps_lock_ != nullptr);
- DCHECK(host_dlopen_handles_lock_ != nullptr);
- DCHECK(intern_table_lock_ != nullptr);
- DCHECK(jni_function_table_lock_ != nullptr);
- DCHECK(jni_libraries_lock_ != nullptr);
- DCHECK(logging_lock_ != nullptr);
- DCHECK(mutator_lock_ != nullptr);
- DCHECK(profiler_lock_ != nullptr);
- DCHECK(cha_lock_ != nullptr);
- DCHECK(subtype_check_lock_ != nullptr);
- DCHECK(thread_list_lock_ != nullptr);
- DCHECK(thread_suspend_count_lock_ != nullptr);
- DCHECK(trace_lock_ != nullptr);
- DCHECK(unexpected_signal_lock_ != nullptr);
- DCHECK(user_code_suspension_lock_ != nullptr);
- DCHECK(dex_lock_ != nullptr);
- DCHECK(native_debug_interface_lock_ != nullptr);
- } else {
- // Create global locks in level order from highest lock level to lowest.
- LockLevel current_lock_level = kInstrumentEntrypointsLock;
- DCHECK(instrument_entrypoints_lock_ == nullptr);
- instrument_entrypoints_lock_ = new Mutex("instrument entrypoint lock", current_lock_level);
-
- #define UPDATE_CURRENT_LOCK_LEVEL(new_level) \
- if ((new_level) >= current_lock_level) { \
- /* Do not use CHECKs or FATAL here, abort_lock_ is not setup yet. */ \
- fprintf(stderr, "New local level %d is not less than current level %d\n", \
- new_level, current_lock_level); \
- exit(1); \
- } \
- current_lock_level = new_level;
-
- UPDATE_CURRENT_LOCK_LEVEL(kUserCodeSuspensionLock);
- DCHECK(user_code_suspension_lock_ == nullptr);
- user_code_suspension_lock_ = new Mutex("user code suspension lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kMutatorLock);
- DCHECK(mutator_lock_ == nullptr);
- mutator_lock_ = new MutatorMutex("mutator lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kHeapBitmapLock);
- DCHECK(heap_bitmap_lock_ == nullptr);
- heap_bitmap_lock_ = new ReaderWriterMutex("heap bitmap lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kTraceLock);
- DCHECK(trace_lock_ == nullptr);
- trace_lock_ = new Mutex("trace lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kRuntimeShutdownLock);
- DCHECK(runtime_shutdown_lock_ == nullptr);
- runtime_shutdown_lock_ = new Mutex("runtime shutdown lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kProfilerLock);
- DCHECK(profiler_lock_ == nullptr);
- profiler_lock_ = new Mutex("profiler lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kDeoptimizationLock);
- DCHECK(deoptimization_lock_ == nullptr);
- deoptimization_lock_ = new Mutex("Deoptimization lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kAllocTrackerLock);
- DCHECK(alloc_tracker_lock_ == nullptr);
- alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock);
- DCHECK(thread_list_lock_ == nullptr);
- thread_list_lock_ = new Mutex("thread list lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kJniLoadLibraryLock);
- DCHECK(jni_libraries_lock_ == nullptr);
- jni_libraries_lock_ = new Mutex("JNI shared libraries map lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kBreakpointLock);
- DCHECK(breakpoint_lock_ == nullptr);
- breakpoint_lock_ = new ReaderWriterMutex("breakpoint lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kSubtypeCheckLock);
- DCHECK(subtype_check_lock_ == nullptr);
- subtype_check_lock_ = new Mutex("SubtypeCheck lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kClassLinkerClassesLock);
- DCHECK(classlinker_classes_lock_ == nullptr);
- classlinker_classes_lock_ = new ReaderWriterMutex("ClassLinker classes lock",
- current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kMonitorPoolLock);
- DCHECK(allocated_monitor_ids_lock_ == nullptr);
- allocated_monitor_ids_lock_ = new Mutex("allocated monitor ids lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kAllocatedThreadIdsLock);
- DCHECK(allocated_thread_ids_lock_ == nullptr);
- allocated_thread_ids_lock_ = new Mutex("allocated thread ids lock", current_lock_level);
-
- if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) {
- UPDATE_CURRENT_LOCK_LEVEL(kModifyLdtLock);
- DCHECK(modify_ldt_lock_ == nullptr);
- modify_ldt_lock_ = new Mutex("modify_ldt lock", current_lock_level);
- }
-
- UPDATE_CURRENT_LOCK_LEVEL(kDexLock);
- DCHECK(dex_lock_ == nullptr);
- dex_lock_ = new ReaderWriterMutex("ClassLinker dex lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kOatFileManagerLock);
- DCHECK(oat_file_manager_lock_ == nullptr);
- oat_file_manager_lock_ = new ReaderWriterMutex("OatFile manager lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kVerifierDepsLock);
- DCHECK(verifier_deps_lock_ == nullptr);
- verifier_deps_lock_ = new ReaderWriterMutex("verifier deps lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kHostDlOpenHandlesLock);
- DCHECK(host_dlopen_handles_lock_ == nullptr);
- host_dlopen_handles_lock_ = new Mutex("host dlopen handles lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kInternTableLock);
- DCHECK(intern_table_lock_ == nullptr);
- intern_table_lock_ = new Mutex("InternTable lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kReferenceProcessorLock);
- DCHECK(reference_processor_lock_ == nullptr);
- reference_processor_lock_ = new Mutex("ReferenceProcessor lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueClearedReferencesLock);
- DCHECK(reference_queue_cleared_references_lock_ == nullptr);
- reference_queue_cleared_references_lock_ = new Mutex("ReferenceQueue cleared references lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueWeakReferencesLock);
- DCHECK(reference_queue_weak_references_lock_ == nullptr);
- reference_queue_weak_references_lock_ = new Mutex("ReferenceQueue cleared references lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueFinalizerReferencesLock);
- DCHECK(reference_queue_finalizer_references_lock_ == nullptr);
- reference_queue_finalizer_references_lock_ = new Mutex("ReferenceQueue finalizer references lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueuePhantomReferencesLock);
- DCHECK(reference_queue_phantom_references_lock_ == nullptr);
- reference_queue_phantom_references_lock_ = new Mutex("ReferenceQueue phantom references lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueSoftReferencesLock);
- DCHECK(reference_queue_soft_references_lock_ == nullptr);
- reference_queue_soft_references_lock_ = new Mutex("ReferenceQueue soft references lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kJniGlobalsLock);
- DCHECK(jni_globals_lock_ == nullptr);
- jni_globals_lock_ =
- new ReaderWriterMutex("JNI global reference table lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kJniWeakGlobalsLock);
- DCHECK(jni_weak_globals_lock_ == nullptr);
- jni_weak_globals_lock_ = new Mutex("JNI weak global reference table lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kJniFunctionTableLock);
- DCHECK(jni_function_table_lock_ == nullptr);
- jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kCustomTlsLock);
- DCHECK(custom_tls_lock_ == nullptr);
- custom_tls_lock_ = new Mutex("Thread::custom_tls_ lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kCHALock);
- DCHECK(cha_lock_ == nullptr);
- cha_lock_ = new Mutex("CHA lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kNativeDebugInterfaceLock);
- DCHECK(native_debug_interface_lock_ == nullptr);
- native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
- DCHECK(abort_lock_ == nullptr);
- abort_lock_ = new Mutex("abort lock", current_lock_level, true);
-
- UPDATE_CURRENT_LOCK_LEVEL(kThreadSuspendCountLock);
- DCHECK(thread_suspend_count_lock_ == nullptr);
- thread_suspend_count_lock_ = new Mutex("thread suspend count lock", current_lock_level);
-
- UPDATE_CURRENT_LOCK_LEVEL(kUnexpectedSignalLock);
- DCHECK(unexpected_signal_lock_ == nullptr);
- unexpected_signal_lock_ = new Mutex("unexpected signal lock", current_lock_level, true);
-
- UPDATE_CURRENT_LOCK_LEVEL(kLoggingLock);
- DCHECK(logging_lock_ == nullptr);
- logging_lock_ = new Mutex("logging lock", current_lock_level, true);
-
- #undef UPDATE_CURRENT_LOCK_LEVEL
-
- // List of mutexes that we may hold when accessing a weak ref.
- AddToExpectedMutexesOnWeakRefAccess(dex_lock_, /*need_lock=*/ false);
- AddToExpectedMutexesOnWeakRefAccess(classlinker_classes_lock_, /*need_lock=*/ false);
- AddToExpectedMutexesOnWeakRefAccess(jni_libraries_lock_, /*need_lock=*/ false);
-
- InitConditions();
- }
-}
-
-void Locks::InitConditions() {
- thread_exit_cond_ = new ConditionVariable("thread exit condition variable", *thread_list_lock_);
-}
-
-void Locks::SetClientCallback(ClientCallback* safe_to_call_abort_cb) {
- safe_to_call_abort_callback.store(safe_to_call_abort_cb, std::memory_order_release);
-}
-
-// Helper to allow checking shutdown while ignoring locking requirements.
-bool Locks::IsSafeToCallAbortRacy() {
- Locks::ClientCallback* safe_to_call_abort_cb =
- safe_to_call_abort_callback.load(std::memory_order_acquire);
- return safe_to_call_abort_cb != nullptr && safe_to_call_abort_cb();
-}
-
-void Locks::AddToExpectedMutexesOnWeakRefAccess(BaseMutex* mutex, bool need_lock) {
- if (need_lock) {
- ScopedExpectedMutexesOnWeakRefAccessLock mu(mutex);
- mutex->SetShouldRespondToEmptyCheckpointRequest(true);
- expected_mutexes_on_weak_ref_access_.push_back(mutex);
- } else {
- mutex->SetShouldRespondToEmptyCheckpointRequest(true);
- expected_mutexes_on_weak_ref_access_.push_back(mutex);
- }
-}
-
-void Locks::RemoveFromExpectedMutexesOnWeakRefAccess(BaseMutex* mutex, bool need_lock) {
- if (need_lock) {
- ScopedExpectedMutexesOnWeakRefAccessLock mu(mutex);
- mutex->SetShouldRespondToEmptyCheckpointRequest(false);
- std::vector<BaseMutex*>& list = expected_mutexes_on_weak_ref_access_;
- auto it = std::find(list.begin(), list.end(), mutex);
- DCHECK(it != list.end());
- list.erase(it);
- } else {
- mutex->SetShouldRespondToEmptyCheckpointRequest(false);
- std::vector<BaseMutex*>& list = expected_mutexes_on_weak_ref_access_;
- auto it = std::find(list.begin(), list.end(), mutex);
- DCHECK(it != list.end());
- list.erase(it);
- }
-}
-
-bool Locks::IsExpectedOnWeakRefAccess(BaseMutex* mutex) {
- ScopedExpectedMutexesOnWeakRefAccessLock mu(mutex);
- std::vector<BaseMutex*>& list = expected_mutexes_on_weak_ref_access_;
- return std::find(list.begin(), list.end(), mutex) != list.end();
-}
-
} // namespace art
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index e391a15..41a47af 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -30,6 +30,7 @@
#include "base/atomic.h"
#include "base/globals.h"
#include "base/macros.h"
+#include "locks.h"
#if defined(__linux__)
#define ART_USE_FUTEXES 1
@@ -50,112 +51,7 @@
class SHARED_LOCKABLE MutatorMutex;
class ScopedContentionRecorder;
class Thread;
-class Mutex;
-
-// LockLevel is used to impose a lock hierarchy [1] where acquisition of a Mutex at a higher or
-// equal level to a lock a thread holds is invalid. The lock hierarchy achieves a cycle free
-// partial ordering and thereby cause deadlock situations to fail checks.
-//
-// [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163
-enum LockLevel : uint8_t {
- kLoggingLock = 0,
- kSwapMutexesLock,
- kUnexpectedSignalLock,
- kThreadSuspendCountLock,
- kAbortLock,
- kNativeDebugInterfaceLock,
- kSignalHandlingLock,
- // A generic lock level for mutexs that should not allow any additional mutexes to be gained after
- // acquiring it.
- kGenericBottomLock,
- // Tracks the second acquisition at the same lock level for kThreadWaitLock. This is an exception
- // to the normal lock ordering, used to implement Monitor::Wait - while holding one kThreadWait
- // level lock, it is permitted to acquire a second one - with internal safeguards to ensure that
- // the second lock acquisition does not result in deadlock. This is implemented in the lock
- // order by treating the second acquisition of a kThreadWaitLock as a kThreadWaitWakeLock
- // acquisition. Thus, acquiring kThreadWaitWakeLock requires holding kThreadWaitLock. This entry
- // is here near the bottom of the hierarchy because other locks should not be
- // acquired while it is held. kThreadWaitLock cannot be moved here because GC
- // activity acquires locks while holding the wait lock.
- kThreadWaitWakeLock,
- kJdwpAdbStateLock,
- kJdwpSocketLock,
- kRegionSpaceRegionLock,
- kMarkSweepMarkStackLock,
- // Can be held while GC related work is done, and thus must be above kMarkSweepMarkStackLock
- kThreadWaitLock,
- kCHALock,
- kJitCodeCacheLock,
- kRosAllocGlobalLock,
- kRosAllocBracketLock,
- kRosAllocBulkFreeLock,
- kTaggingLockLevel,
- kTransactionLogLock,
- kCustomTlsLock,
- kJniFunctionTableLock,
- kJniWeakGlobalsLock,
- kJniGlobalsLock,
- kReferenceQueueSoftReferencesLock,
- kReferenceQueuePhantomReferencesLock,
- kReferenceQueueFinalizerReferencesLock,
- kReferenceQueueWeakReferencesLock,
- kReferenceQueueClearedReferencesLock,
- kReferenceProcessorLock,
- kJitDebugInterfaceLock,
- kAllocSpaceLock,
- kBumpPointerSpaceBlockLock,
- kArenaPoolLock,
- kInternTableLock,
- kOatFileSecondaryLookupLock,
- kHostDlOpenHandlesLock,
- kVerifierDepsLock,
- kOatFileManagerLock,
- kTracingUniqueMethodsLock,
- kTracingStreamingLock,
- kClassLoaderClassesLock,
- kDefaultMutexLevel,
- kDexLock,
- kMarkSweepLargeObjectLock,
- kJdwpObjectRegistryLock,
- kModifyLdtLock,
- kAllocatedThreadIdsLock,
- kMonitorPoolLock,
- kClassLinkerClassesLock, // TODO rename.
- kDexToDexCompilerLock,
- kSubtypeCheckLock,
- kBreakpointLock,
- kMonitorLock,
- kMonitorListLock,
- kJniLoadLibraryLock,
- kThreadListLock,
- kAllocTrackerLock,
- kDeoptimizationLock,
- kProfilerLock,
- kJdwpShutdownLock,
- kJdwpEventListLock,
- kJdwpAttachLock,
- kJdwpStartLock,
- kRuntimeShutdownLock,
- kTraceLock,
- kHeapBitmapLock,
- kMutatorLock,
- kUserCodeSuspensionLock,
- kInstrumentEntrypointsLock,
- kZygoteCreationLock,
-
- // The highest valid lock level. Use this if there is code that should only be called with no
- // other locks held. Since this is the highest lock level we also allow it to be held even if the
- // runtime or current thread is not fully set-up yet (for example during thread attach). Note that
- // this lock also has special behavior around the mutator_lock_. Since the mutator_lock_ is not
- // really a 'real' lock we allow this to be locked when the mutator_lock_ is held exclusive.
- // Furthermore, the mutator_lock_ may not be acquired in any form when a lock of this level is
- // held. Since the mutator_lock_ being held strong means that all other threads are suspended this
- // will prevent deadlocks while still allowing this lock level to function as a "highest" level.
- kTopLockLevel,
-
- kLockLevelCount // Must come last.
-};
-std::ostream& operator<<(std::ostream& os, const LockLevel& rhs);
+class LOCKABLE Mutex;
constexpr bool kDebugLocking = kIsDebugBuild;
@@ -578,226 +474,6 @@
DISALLOW_COPY_AND_ASSIGN(WriterMutexLock);
};
-// For StartNoThreadSuspension and EndNoThreadSuspension.
-class CAPABILITY("role") Role {
- public:
- void Acquire() ACQUIRE() {}
- void Release() RELEASE() {}
- const Role& operator!() const { return *this; }
-};
-
-class Uninterruptible : public Role {
-};
-
-// Global mutexes corresponding to the levels above.
-class Locks {
- public:
- static void Init();
- static void InitConditions() NO_THREAD_SAFETY_ANALYSIS; // Condition variables.
-
- // Destroying various lock types can emit errors that vary depending upon
- // whether the client (art::Runtime) is currently active. Allow the client
- // to set a callback that is used to check when it is acceptable to call
- // Abort. The default behavior is that the client *is not* able to call
- // Abort if no callback is established.
- using ClientCallback = bool();
- static void SetClientCallback(ClientCallback* is_safe_to_call_abort_cb) NO_THREAD_SAFETY_ANALYSIS;
- // Checks for whether it is safe to call Abort() without using locks.
- static bool IsSafeToCallAbortRacy() NO_THREAD_SAFETY_ANALYSIS;
-
- // Add a mutex to expected_mutexes_on_weak_ref_access_.
- static void AddToExpectedMutexesOnWeakRefAccess(BaseMutex* mutex, bool need_lock = true);
- // Remove a mutex from expected_mutexes_on_weak_ref_access_.
- static void RemoveFromExpectedMutexesOnWeakRefAccess(BaseMutex* mutex, bool need_lock = true);
- // Check if the given mutex is in expected_mutexes_on_weak_ref_access_.
- static bool IsExpectedOnWeakRefAccess(BaseMutex* mutex);
-
- // Guards allocation entrypoint instrumenting.
- static Mutex* instrument_entrypoints_lock_;
-
- // Guards code that deals with user-code suspension. This mutex must be held when suspending or
- // resuming threads with SuspendReason::kForUserCode. It may be held by a suspended thread, but
- // only if the suspension is not due to SuspendReason::kForUserCode.
- static Mutex* user_code_suspension_lock_ ACQUIRED_AFTER(instrument_entrypoints_lock_);
-
- // A barrier is used to synchronize the GC/Debugger thread with mutator threads. When GC/Debugger
- // thread wants to suspend all mutator threads, it needs to wait for all mutator threads to pass
- // a barrier. Threads that are already suspended will get their barrier passed by the GC/Debugger
- // thread; threads in the runnable state will pass the barrier when they transit to the suspended
- // state. GC/Debugger thread will be woken up when all mutator threads are suspended.
- //
- // Thread suspension:
- // mutator thread | GC/Debugger
- // .. running .. | .. running ..
- // .. running .. | Request thread suspension by:
- // .. running .. | - acquiring thread_suspend_count_lock_
- // .. running .. | - incrementing Thread::suspend_count_ on
- // .. running .. | all mutator threads
- // .. running .. | - releasing thread_suspend_count_lock_
- // .. running .. | Block wait for all threads to pass a barrier
- // Poll Thread::suspend_count_ and enter full | .. blocked ..
- // suspend code. | .. blocked ..
- // Change state to kSuspended (pass the barrier) | Wake up when all threads pass the barrier
- // x: Acquire thread_suspend_count_lock_ | .. running ..
- // while Thread::suspend_count_ > 0 | .. running ..
- // - wait on Thread::resume_cond_ | .. running ..
- // (releases thread_suspend_count_lock_) | .. running ..
- // .. waiting .. | Request thread resumption by:
- // .. waiting .. | - acquiring thread_suspend_count_lock_
- // .. waiting .. | - decrementing Thread::suspend_count_ on
- // .. waiting .. | all mutator threads
- // .. waiting .. | - notifying on Thread::resume_cond_
- // - re-acquire thread_suspend_count_lock_ | - releasing thread_suspend_count_lock_
- // Release thread_suspend_count_lock_ | .. running ..
- // Change to kRunnable | .. running ..
- // - this uses a CAS operation to ensure the | .. running ..
- // suspend request flag isn't raised as the | .. running ..
- // state is changed | .. running ..
- // - if the CAS operation fails then goto x | .. running ..
- // .. running .. | .. running ..
- static MutatorMutex* mutator_lock_ ACQUIRED_AFTER(user_code_suspension_lock_);
-
- // Allow reader-writer mutual exclusion on the mark and live bitmaps of the heap.
- static ReaderWriterMutex* heap_bitmap_lock_ ACQUIRED_AFTER(mutator_lock_);
-
- // Guards shutdown of the runtime.
- static Mutex* runtime_shutdown_lock_ ACQUIRED_AFTER(heap_bitmap_lock_);
-
- // Guards background profiler global state.
- static Mutex* profiler_lock_ ACQUIRED_AFTER(runtime_shutdown_lock_);
-
- // Guards trace (ie traceview) requests.
- static Mutex* trace_lock_ ACQUIRED_AFTER(profiler_lock_);
-
- // Guards debugger recent allocation records.
- static Mutex* alloc_tracker_lock_ ACQUIRED_AFTER(trace_lock_);
-
- // Guards updates to instrumentation to ensure mutual exclusion of
- // events like deoptimization requests.
- // TODO: improve name, perhaps instrumentation_update_lock_.
- static Mutex* deoptimization_lock_ ACQUIRED_AFTER(alloc_tracker_lock_);
-
- // Guard the update of the SubtypeCheck data stores in each Class::status_ field.
- // This lock is used in SubtypeCheck methods which are the interface for
- // any SubtypeCheck-mutating methods.
- // In Class::IsSubClass, the lock is not required since it does not update the SubtypeCheck data.
- static Mutex* subtype_check_lock_ ACQUIRED_AFTER(deoptimization_lock_);
-
- // The thread_list_lock_ guards ThreadList::list_. It is also commonly held to stop threads
- // attaching and detaching.
- static Mutex* thread_list_lock_ ACQUIRED_AFTER(subtype_check_lock_);
-
- // Signaled when threads terminate. Used to determine when all non-daemons have terminated.
- static ConditionVariable* thread_exit_cond_ GUARDED_BY(Locks::thread_list_lock_);
-
- // Guards maintaining loading library data structures.
- static Mutex* jni_libraries_lock_ ACQUIRED_AFTER(thread_list_lock_);
-
- // Guards breakpoints.
- static ReaderWriterMutex* breakpoint_lock_ ACQUIRED_AFTER(jni_libraries_lock_);
-
- // Guards lists of classes within the class linker.
- static ReaderWriterMutex* classlinker_classes_lock_ ACQUIRED_AFTER(breakpoint_lock_);
-
- // When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code
- // doesn't try to hold a higher level Mutex.
- #define DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(art::Locks::classlinker_classes_lock_)
-
- static Mutex* allocated_monitor_ids_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
-
- // Guard the allocation/deallocation of thread ids.
- static Mutex* allocated_thread_ids_lock_ ACQUIRED_AFTER(allocated_monitor_ids_lock_);
-
- // Guards modification of the LDT on x86.
- static Mutex* modify_ldt_lock_ ACQUIRED_AFTER(allocated_thread_ids_lock_);
-
- static ReaderWriterMutex* dex_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
-
- // Guards opened oat files in OatFileManager.
- static ReaderWriterMutex* oat_file_manager_lock_ ACQUIRED_AFTER(dex_lock_);
-
- // Guards extra string entries for VerifierDeps.
- static ReaderWriterMutex* verifier_deps_lock_ ACQUIRED_AFTER(oat_file_manager_lock_);
-
- // Guards dlopen_handles_ in DlOpenOatFile.
- static Mutex* host_dlopen_handles_lock_ ACQUIRED_AFTER(verifier_deps_lock_);
-
- // Guards intern table.
- static Mutex* intern_table_lock_ ACQUIRED_AFTER(host_dlopen_handles_lock_);
-
- // Guards reference processor.
- static Mutex* reference_processor_lock_ ACQUIRED_AFTER(intern_table_lock_);
-
- // Guards cleared references queue.
- static Mutex* reference_queue_cleared_references_lock_ ACQUIRED_AFTER(reference_processor_lock_);
-
- // Guards weak references queue.
- static Mutex* reference_queue_weak_references_lock_ ACQUIRED_AFTER(reference_queue_cleared_references_lock_);
-
- // Guards finalizer references queue.
- static Mutex* reference_queue_finalizer_references_lock_ ACQUIRED_AFTER(reference_queue_weak_references_lock_);
-
- // Guards phantom references queue.
- static Mutex* reference_queue_phantom_references_lock_ ACQUIRED_AFTER(reference_queue_finalizer_references_lock_);
-
- // Guards soft references queue.
- static Mutex* reference_queue_soft_references_lock_ ACQUIRED_AFTER(reference_queue_phantom_references_lock_);
-
- // Guard accesses to the JNI Global Reference table.
- static ReaderWriterMutex* jni_globals_lock_ ACQUIRED_AFTER(reference_queue_soft_references_lock_);
-
- // Guard accesses to the JNI Weak Global Reference table.
- static Mutex* jni_weak_globals_lock_ ACQUIRED_AFTER(jni_globals_lock_);
-
- // Guard accesses to the JNI function table override.
- static Mutex* jni_function_table_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
-
- // Guard accesses to the Thread::custom_tls_. We use this to allow the TLS of other threads to be
- // read (the reader must hold the ThreadListLock or have some other way of ensuring the thread
- // will not die in that case though). This is useful for (eg) the implementation of
- // GetThreadLocalStorage.
- static Mutex* custom_tls_lock_ ACQUIRED_AFTER(jni_function_table_lock_);
-
- // Guards Class Hierarchy Analysis (CHA).
- static Mutex* cha_lock_ ACQUIRED_AFTER(custom_tls_lock_);
-
- // When declaring any Mutex add BOTTOM_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code
- // doesn't try to acquire a higher level Mutex. NB Due to the way the annotalysis works this
- // actually only encodes the mutex being below jni_function_table_lock_ although having
- // kGenericBottomLock level is lower than this.
- #define BOTTOM_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(art::Locks::cha_lock_)
-
- // Have an exclusive aborting thread.
- static Mutex* abort_lock_ ACQUIRED_AFTER(custom_tls_lock_);
-
- // Allow mutual exclusion when manipulating Thread::suspend_count_.
- // TODO: Does the trade-off of a per-thread lock make sense?
- static Mutex* thread_suspend_count_lock_ ACQUIRED_AFTER(abort_lock_);
-
- // One unexpected signal at a time lock.
- static Mutex* unexpected_signal_lock_ ACQUIRED_AFTER(thread_suspend_count_lock_);
-
- // Guards the magic global variables used by native tools (e.g. libunwind).
- static Mutex* native_debug_interface_lock_ ACQUIRED_AFTER(unexpected_signal_lock_);
-
- // Have an exclusive logging thread.
- static Mutex* logging_lock_ ACQUIRED_AFTER(native_debug_interface_lock_);
-
- // List of mutexes that we expect a thread may hold when accessing weak refs. This is used to
- // avoid a deadlock in the empty checkpoint while weak ref access is disabled (b/34964016). If we
- // encounter an unexpected mutex on accessing weak refs,
- // Thread::CheckEmptyCheckpointFromWeakRefAccess will detect it.
- static std::vector<BaseMutex*> expected_mutexes_on_weak_ref_access_;
- static Atomic<const BaseMutex*> expected_mutexes_on_weak_ref_access_guard_;
- class ScopedExpectedMutexesOnWeakRefAccessLock;
-};
-
-class Roles {
- public:
- // Uninterruptible means that the thread may not become suspended.
- static Uninterruptible uninterruptible_;
-};
-
} // namespace art
#endif // ART_RUNTIME_BASE_MUTEX_H_
diff --git a/runtime/cha.cc b/runtime/cha.cc
index 8e06fda..5110b7a 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -18,6 +18,7 @@
#include "art_method-inl.h"
#include "base/logging.h" // For VLOG
+#include "base/mutex.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "linear_alloc.h"
diff --git a/runtime/cha.h b/runtime/cha.h
index d1a1b7c..a07ee91 100644
--- a/runtime/cha.h
+++ b/runtime/cha.h
@@ -21,7 +21,7 @@
#include <unordered_set>
#include "base/enums.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "handle.h"
#include "mirror/class.h"
#include "oat_quick_method_header.h"
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 6703205..43f3ed3 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -21,6 +21,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
+#include "base/mutex.h"
#include "class_linker.h"
#include "gc_root-inl.h"
#include "handle_scope-inl.h"
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index dd5f911..d3eab7c 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -25,8 +25,8 @@
#include <vector>
#include "base/enums.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "dex/class_accessor.h"
#include "dex/dex_cache_resolved_classes.h"
#include "dex/dex_file.h"
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index 945d659..69476df 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -18,7 +18,7 @@
#define ART_RUNTIME_CLASS_LOADER_UTILS_H_
#include "art_field-inl.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "handle_scope.h"
#include "jni/jni_internal.h"
#include "mirror/class_loader.h"
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index 15ab5f0..5c5431d 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -20,8 +20,8 @@
#include "android-base/logging.h"
#include "art_field.h"
#include "art_method.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "class_linker.h"
#include "dex/code_item_accessors.h"
#include "dex/primitive.h"
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 319c7c7..29b7813 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -27,7 +27,7 @@
#include "arch/instruction_set.h"
#include "base/common_art_test.h"
#include "base/globals.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "base/os.h"
#include "base/unix_file/fd_file.h"
#include "dex/art_dex_file_loader.h"
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 6acff6f..ca9c96a 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_COMMON_THROWS_H_
#define ART_RUNTIME_COMMON_THROWS_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "obj_ptr.h"
namespace art {
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
index b29eb70..18632dc 100644
--- a/runtime/compiler_callbacks.h
+++ b/runtime/compiler_callbacks.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_COMPILER_CALLBACKS_H_
#define ART_RUNTIME_COMPILER_CALLBACKS_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "dex/class_reference.h"
#include "class_status.h"
diff --git a/runtime/debug_print.h b/runtime/debug_print.h
index df00f06..e2990d4 100644
--- a/runtime/debug_print.h
+++ b/runtime/debug_print.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_DEBUG_PRINT_H_
#define ART_RUNTIME_DEBUG_PRINT_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "mirror/object.h"
// Helper functions for printing extra information for certain hard to diagnose bugs.
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index c8bf6d0..e10a6e8 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -21,8 +21,8 @@
#include <stdint.h>
#include "base/callee_save_type.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "dex/dex_file_types.h"
#include "dex/dex_instruction.h"
#include "gc/allocator_type.h"
diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h
index 1e30907..e555d68 100644
--- a/runtime/entrypoints/quick/callee_save_frame.h
+++ b/runtime/entrypoints/quick/callee_save_frame.h
@@ -20,7 +20,7 @@
#include "arch/instruction_set.h"
#include "base/callee_save_type.h"
#include "base/enums.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "quick/quick_method_frame_info.h"
#include "thread-inl.h"
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.h b/runtime/entrypoints/quick/quick_alloc_entrypoints.h
index bd1e295..937ba8e 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ALLOC_ENTRYPOINTS_H_
#define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ALLOC_ENTRYPOINTS_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "gc/allocator_type.h"
#include "quick_entrypoints.h"
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index 795faa8..243f7ec 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -19,8 +19,8 @@
#include <jni.h>
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "deoptimization_kind.h"
#include "offsets.h"
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 24ef0b1..d3be51f 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -23,7 +23,8 @@
#include <vector>
-#include "base/mutex.h" // For annotalysis.
+#include "base/globals.h" // For CanDoImplicitNullCheckOn.
+#include "base/locks.h" // For annotalysis.
namespace art {
diff --git a/runtime/gc/accounting/bitmap.h b/runtime/gc/accounting/bitmap.h
index ffef566..bdc686e 100644
--- a/runtime/gc/accounting/bitmap.h
+++ b/runtime/gc/accounting/bitmap.h
@@ -24,8 +24,8 @@
#include <vector>
#include "base/globals.h"
+#include "base/locks.h"
#include "base/mem_map.h"
-#include "base/mutex.h"
namespace art {
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index f163898..b84f22f 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -20,8 +20,8 @@
#include <memory>
#include "base/globals.h"
+#include "base/locks.h"
#include "base/mem_map.h"
-#include "base/mutex.h"
namespace art {
diff --git a/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h
index c997f8d..e477556 100644
--- a/runtime/gc/accounting/heap_bitmap.h
+++ b/runtime/gc/accounting/heap_bitmap.h
@@ -20,8 +20,8 @@
#include <android-base/logging.h>
#include "base/allocator.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "space_bitmap.h"
namespace art {
diff --git a/runtime/gc/accounting/read_barrier_table.h b/runtime/gc/accounting/read_barrier_table.h
index 7eca792..2e42f8d 100644
--- a/runtime/gc/accounting/read_barrier_table.h
+++ b/runtime/gc/accounting/read_barrier_table.h
@@ -21,8 +21,8 @@
#include "base/bit_utils.h"
#include "base/globals.h"
+#include "base/locks.h"
#include "base/mem_map.h"
-#include "base/mutex.h"
#include "gc/space/space.h"
namespace art {
diff --git a/runtime/gc/accounting/remembered_set.h b/runtime/gc/accounting/remembered_set.h
index b96f0d3..469074f 100644
--- a/runtime/gc/accounting/remembered_set.h
+++ b/runtime/gc/accounting/remembered_set.h
@@ -19,7 +19,7 @@
#include "base/allocator.h"
#include "base/globals.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "base/safe_map.h"
#include <set>
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index fcc3007..8561f06 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -24,8 +24,8 @@
#include <vector>
#include "base/globals.h"
+#include "base/locks.h"
#include "base/mem_map.h"
-#include "base/mutex.h"
namespace art {
diff --git a/runtime/gc/allocation_listener.h b/runtime/gc/allocation_listener.h
index 7675a22..a578252 100644
--- a/runtime/gc/allocation_listener.h
+++ b/runtime/gc/allocation_listener.h
@@ -20,8 +20,8 @@
#include <list>
#include <memory>
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "gc_root.h"
#include "obj_ptr.h"
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index 6b394c7..3160422 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -242,15 +242,33 @@
// Use load-acquire on the read barrier pointer to ensure that we never see a black (non-gray)
// read barrier state with an unmarked bit due to reordering.
DCHECK(region_space_->IsInUnevacFromSpace(from_ref));
- if (kEnableGenerationalConcurrentCopyingCollection
- && young_gen_
- && !done_scanning_.load(std::memory_order_acquire)) {
- return from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState();
- }
if (kUseBakerReadBarrier && from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState()) {
return true;
+ } else if (!(kEnableGenerationalConcurrentCopyingCollection && young_gen_)
+ || done_scanning_.load(std::memory_order_acquire)) {
+ // If the card table scanning is not finished yet, then only read-barrier
+ // state should be checked. Checking the mark bitmap is unreliable as there
+ // may be some objects - whose corresponding card is dirty - which are
+ // marked in the mark bitmap, but cannot be considered marked unless their
+ // read-barrier state is set to Gray.
+ //
+ // Why read read-barrier state before checking done_scanning_?
+ // If the read-barrier state was read *after* done_scanning_, then there
+ // exists a concurrency race due to which even after the object is marked,
+ // read-barrier state is checked *after* that, this function will return
+ // false. The following scenario may cause the race:
+ //
+ // 1. Mutator thread reads done_scanning_ and upon finding it false, gets
+ // suspended before reading the object's read-barrier state.
+ // 2. GC thread finishes card-table scan and then sets done_scanning_ to
+ // true.
+ // 3. GC thread grays the object, scans it, marks in the bitmap, and then
+ // changes its read-barrier state back to non-gray.
+ // 4. Mutator thread resumes, reads the object's read-barrier state and
+ // returns false.
+ return region_space_bitmap_->Test(from_ref);
}
- return region_space_bitmap_->Test(from_ref);
+ return false;
}
} // namespace collector
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index fefe9ab..7736568 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -332,11 +332,6 @@
<< reinterpret_cast<void*>(region_space_->Limit());
}
CheckEmptyMarkStack();
- if (kIsDebugBuild) {
- MutexLock mu(Thread::Current(), mark_stack_lock_);
- CHECK(false_gray_stack_.empty());
- }
-
rb_mark_bit_stack_full_ = false;
mark_from_read_barrier_measurements_ = measure_read_barrier_slow_path_;
if (measure_read_barrier_slow_path_) {
@@ -1056,9 +1051,6 @@
Runtime::Current()->GetClassLinker()->CleanupClassLoaders();
// Marking is done. Disable marking.
DisableMarking();
- if (kUseBakerReadBarrier) {
- ProcessFalseGrayStack();
- }
CheckEmptyMarkStack();
}
@@ -1170,32 +1162,6 @@
mark_stack_mode_.store(kMarkStackModeOff, std::memory_order_seq_cst);
}
-void ConcurrentCopying::PushOntoFalseGrayStack(Thread* const self, mirror::Object* ref) {
- CHECK(kUseBakerReadBarrier);
- DCHECK(ref != nullptr);
- MutexLock mu(self, mark_stack_lock_);
- false_gray_stack_.push_back(ref);
-}
-
-void ConcurrentCopying::ProcessFalseGrayStack() {
- CHECK(kUseBakerReadBarrier);
- // Change the objects on the false gray stack from gray to non-gray (conceptually black).
- MutexLock mu(Thread::Current(), mark_stack_lock_);
- for (mirror::Object* obj : false_gray_stack_) {
- DCHECK(IsMarked(obj));
- // The object could be non-gray (conceptually black) here if a thread got preempted after a
- // success at the AtomicSetReadBarrierState in MarkNonMoving(), GC started marking through it
- // (but not finished so still gray), the thread ran to register it onto the false gray stack,
- // and then GC eventually marked it black (non-gray) after it finished scanning it.
- if (obj->GetReadBarrierState() == ReadBarrier::GrayState()) {
- bool success = obj->AtomicSetReadBarrierState(ReadBarrier::GrayState(),
- ReadBarrier::NonGrayState());
- DCHECK(success);
- }
- }
- false_gray_stack_.clear();
-}
-
void ConcurrentCopying::IssueEmptyCheckpoint() {
Thread* self = Thread::Current();
ThreadList* thread_list = Runtime::Current()->GetThreadList();
@@ -1418,24 +1384,6 @@
}
// The following visitors are used to assert the to-space invariant.
-class ConcurrentCopying::AssertToSpaceInvariantRefsVisitor {
- public:
- explicit AssertToSpaceInvariantRefsVisitor(ConcurrentCopying* collector)
- : collector_(collector) {}
-
- void operator()(mirror::Object* ref) const
- REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
- if (ref == nullptr) {
- // OK.
- return;
- }
- collector_->AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
- }
-
- private:
- ConcurrentCopying* const collector_;
-};
-
class ConcurrentCopying::AssertToSpaceInvariantFieldVisitor {
public:
explicit AssertToSpaceInvariantFieldVisitor(ConcurrentCopying* collector)
@@ -1447,8 +1395,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
mirror::Object* ref =
obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
- AssertToSpaceInvariantRefsVisitor visitor(collector_);
- visitor(ref);
+ collector_->AssertToSpaceInvariant(obj.Ptr(), offset, ref);
}
void operator()(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> ref ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
@@ -1464,8 +1411,8 @@
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
REQUIRES_SHARED(Locks::mutator_lock_) {
- AssertToSpaceInvariantRefsVisitor visitor(collector_);
- visitor(root->AsMirrorPtr());
+ mirror::Object* ref = root->AsMirrorPtr();
+ collector_->AssertToSpaceInvariant(/* obj */ nullptr, MemberOffset(0), ref);
}
private:
@@ -1671,30 +1618,69 @@
// Invariant: There should be no object from a newly-allocated
// region (either large or non-large) on the mark stack.
DCHECK(!region_space_->IsInNewlyAllocatedRegion(to_ref)) << to_ref;
- if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
- // Mark the bitmap only in the GC thread here so that we don't need a CAS.
- if (!kUseBakerReadBarrier ||
- !region_space_bitmap_->Set(to_ref)) {
- // It may be already marked if we accidentally pushed the same object twice due to the racy
- // bitmap read in MarkUnevacFromSpaceRegion.
- if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
- CHECK(region_space_->IsLargeObject(to_ref));
- region_space_->ZeroLiveBytesForLargeObject(to_ref);
- Scan<true>(to_ref);
- } else {
- Scan<false>(to_ref);
+ bool perform_scan = false;
+ switch (rtype) {
+ case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace:
+ // Mark the bitmap only in the GC thread here so that we don't need a CAS.
+ if (!kUseBakerReadBarrier || !region_space_bitmap_->Set(to_ref)) {
+ // It may be already marked if we accidentally pushed the same object twice due to the racy
+ // bitmap read in MarkUnevacFromSpaceRegion.
+ if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ CHECK(region_space_->IsLargeObject(to_ref));
+ region_space_->ZeroLiveBytesForLargeObject(to_ref);
+ }
+ perform_scan = true;
+ // Only add to the live bytes if the object was not already marked and we are not the young
+ // GC.
+ add_to_live_bytes = true;
}
- // Only add to the live bytes if the object was not already marked and we are not the young
- // GC.
- add_to_live_bytes = true;
- }
- } else {
- if (kEnableGenerationalConcurrentCopyingCollection) {
- if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
+ break;
+ case space::RegionSpace::RegionType::kRegionTypeToSpace:
+ if (kEnableGenerationalConcurrentCopyingCollection) {
// Copied to to-space, set the bit so that the next GC can scan objects.
region_space_bitmap_->Set(to_ref);
}
- }
+ perform_scan = true;
+ break;
+ default:
+ DCHECK(!region_space_->HasAddress(to_ref)) << to_ref;
+ DCHECK(!immune_spaces_.ContainsObject(to_ref));
+ // Non-moving or large-object space.
+ if (kUseBakerReadBarrier) {
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_->GetNonMovingSpace()->GetMarkBitmap();
+ const bool is_los = !mark_bitmap->HasAddress(to_ref);
+ if (is_los) {
+ if (!IsAligned<kPageSize>(to_ref)) {
+ // Ref is a large object that is not aligned, it must be heap
+ // corruption. Remove memory protection and dump data before
+ // AtomicSetReadBarrierState since it will fault if the address is not
+ // valid.
+ region_space_->Unprotect();
+ heap_->GetVerification()->LogHeapCorruption(/* obj */ nullptr,
+ MemberOffset(0),
+ to_ref,
+ /* fatal */ true);
+ }
+ DCHECK(heap_->GetLargeObjectsSpace())
+ << "ref=" << to_ref
+ << " doesn't belong to non-moving space and large object space doesn't exist";
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ DCHECK(los_bitmap->HasAddress(to_ref));
+ // Only the GC thread could be setting the LOS bit map hence doesn't
+ // need to be atomically done.
+ perform_scan = !los_bitmap->Set(to_ref);
+ } else {
+ // Only the GC thread could be setting the non-moving space bit map
+ // hence doesn't need to be atomically done.
+ perform_scan = !mark_bitmap->Set(to_ref);
+ }
+ } else {
+ perform_scan = true;
+ }
+ }
+ if (perform_scan) {
if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
Scan<true>(to_ref);
} else {
@@ -2079,6 +2065,9 @@
LOG(INFO) << "(after) num_bytes_allocated="
<< heap_->num_bytes_allocated_.load();
}
+
+ float reclaimed_bytes_ratio = static_cast<float>(freed_bytes) / num_bytes_allocated_before_gc_;
+ reclaimed_bytes_ratio_sum_ += reclaimed_bytes_ratio;
}
{
@@ -2094,11 +2083,6 @@
CheckEmptyMarkStack();
- int64_t num_bytes_allocated_after_gc = static_cast<int64_t>(heap_->GetBytesAllocated());
- int64_t diff = num_bytes_allocated_before_gc_ - num_bytes_allocated_after_gc;
- auto ratio = static_cast<float>(diff) / num_bytes_allocated_before_gc_;
- reclaimed_bytes_ratio_sum_ += ratio;
-
if (kVerboseMode) {
LOG(INFO) << "GC end of ReclaimPhase";
}
@@ -2145,7 +2129,10 @@
mirror::Object* ref) {
CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
if (is_asserting_to_space_invariant_) {
- if (region_space_->HasAddress(ref)) {
+ if (ref == nullptr) {
+ // OK.
+ return;
+ } else if (region_space_->HasAddress(ref)) {
// Check to-space invariant in region space (moving space).
using RegionType = space::RegionSpace::RegionType;
space::RegionSpace::RegionType type = region_space_->GetRegionTypeUnsafe(ref);
@@ -2248,7 +2235,10 @@
mirror::Object* ref) {
CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
if (is_asserting_to_space_invariant_) {
- if (region_space_->HasAddress(ref)) {
+ if (ref == nullptr) {
+ // OK.
+ return;
+ } else if (region_space_->HasAddress(ref)) {
// Check to-space invariant in region space (moving space).
using RegionType = space::RegionSpace::RegionType;
space::RegionSpace::RegionType type = region_space_->GetRegionTypeUnsafe(ref);
@@ -2326,14 +2316,17 @@
LOG(INFO) << "holder is in an immune image or the zygote space.";
} else {
LOG(INFO) << "holder is in a non-immune, non-moving (or main) space.";
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(obj);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(obj);
- CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
- bool is_los = mark_bitmap == nullptr;
+ accounting::ContinuousSpaceBitmap* mark_bitmap = heap_->GetNonMovingSpace()->GetMarkBitmap();
+ accounting::LargeObjectBitmap* los_bitmap = nullptr;
+ const bool is_los = !mark_bitmap->HasAddress(obj);
+ if (is_los) {
+ DCHECK(heap_->GetLargeObjectsSpace() && heap_->GetLargeObjectsSpace()->Contains(obj))
+ << "obj=" << obj
+ << " LOS bit map covers the entire lower 4GB address range";
+ los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ }
if (!is_los && mark_bitmap->Test(obj)) {
- LOG(INFO) << "holder is marked in the mark bit map.";
+ LOG(INFO) << "holder is marked in the non-moving space mark bit map.";
} else if (is_los && los_bitmap->Test(obj)) {
LOG(INFO) << "holder is marked in the los bit map.";
} else {
@@ -2350,6 +2343,30 @@
LOG(INFO) << "offset=" << offset.SizeValue();
}
+bool ConcurrentCopying::IsMarkedInNonMovingSpace(mirror::Object* from_ref) {
+ DCHECK(!region_space_->HasAddress(from_ref)) << "ref=" << from_ref;
+ DCHECK(!immune_spaces_.ContainsObject(from_ref)) << "ref=" << from_ref;
+ if (kUseBakerReadBarrier && from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState()) {
+ return true;
+ } else if (!(kEnableGenerationalConcurrentCopyingCollection && young_gen_)
+ || done_scanning_.load(std::memory_order_acquire)) {
+ // Read the comment in IsMarkedInUnevacFromSpace()
+ accounting::ContinuousSpaceBitmap* mark_bitmap = heap_->GetNonMovingSpace()->GetMarkBitmap();
+ accounting::LargeObjectBitmap* los_bitmap = nullptr;
+ const bool is_los = !mark_bitmap->HasAddress(from_ref);
+ if (is_los) {
+ DCHECK(heap_->GetLargeObjectsSpace() && heap_->GetLargeObjectsSpace()->Contains(from_ref))
+ << "ref=" << from_ref
+ << " doesn't belong to non-moving space and large object space doesn't exist";
+ los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ }
+ if (is_los ? los_bitmap->Test(from_ref) : mark_bitmap->Test(from_ref)) {
+ return true;
+ }
+ }
+ return IsOnAllocStack(from_ref);
+}
+
void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* obj,
mirror::Object* ref) {
CHECK(ref != nullptr);
@@ -2371,62 +2388,14 @@
}
} else {
// Non-moving space and large-object space (LOS) cases.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(ref);
- bool is_los = (mark_bitmap == nullptr);
-
- bool marked_in_non_moving_space_or_los =
- (kUseBakerReadBarrier
- && kEnableGenerationalConcurrentCopyingCollection
- && young_gen_
- && !done_scanning_.load(std::memory_order_acquire))
- // Don't use the mark bitmap to ensure `ref` is marked: check that the
- // read barrier state is gray instead. This is to take into account a
- // potential race between two read barriers on the same reference when the
- // young-generation collector is still scanning the dirty cards.
- //
- // For instance consider two concurrent read barriers on the same GC root
- // reference during the dirty-card-scanning step of a young-generation
- // collection. Both threads would call ReadBarrier::BarrierForRoot, which
- // would:
- // a. mark the reference (leading to a call to
- // ConcurrentCopying::MarkNonMoving); then
- // b. check the to-space invariant (leading to a call this
- // ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace -- this
- // method).
- //
- // In this situation, the following race could happen:
- // 1. Thread A successfully changes `ref`'s read barrier state from
- // non-gray (white) to gray (with AtomicSetReadBarrierState) in
- // ConcurrentCopying::MarkNonMoving, then gets preempted.
- // 2. Thread B also tries to change `ref`'s read barrier state with
- // AtomicSetReadBarrierState from non-gray to gray in
- // ConcurrentCopying::MarkNonMoving, but fails, as Thread A already
- // changed it.
- // 3. Because Thread B failed the previous CAS, it does *not* set the
- // bit in the mark bitmap for `ref`.
- // 4. Thread B checks the to-space invariant and calls
- // ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace: the bit
- // is not set in the mark bitmap for `ref`; checking that this bit is
- // set to check the to-space invariant is therefore not a reliable
- // test.
- // 5. (Note that eventually, Thread A will resume its execution and set
- // the bit for `ref` in the mark bitmap.)
- ? (ref->GetReadBarrierState() == ReadBarrier::GrayState())
- // It is safe to use the heap mark bitmap otherwise.
- : (!is_los && mark_bitmap->Test(ref)) || (is_los && los_bitmap->Test(ref));
-
// If `ref` is on the allocation stack, then it may not be
// marked live, but considered marked/alive (but not
// necessarily on the live stack).
- CHECK(marked_in_non_moving_space_or_los || IsOnAllocStack(ref))
+ CHECK(IsMarkedInNonMovingSpace(ref))
<< "Unmarked ref that's not on the allocation stack."
<< " obj=" << obj
<< " ref=" << ref
<< " rb_state=" << ref->GetReadBarrierState()
- << " is_los=" << std::boolalpha << is_los << std::noboolalpha
<< " is_marking=" << std::boolalpha << is_marking_ << std::noboolalpha
<< " young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha
<< " done_scanning="
@@ -2780,12 +2749,6 @@
LOG(FATAL) << "Object address=" << from_ref << " type=" << from_ref->PrettyTypeOf();
}
bytes_allocated = non_moving_space_bytes_allocated;
- // Mark it in the mark bitmap.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
- CHECK(mark_bitmap != nullptr);
- bool previously_marked_in_bitmap = mark_bitmap->AtomicTestAndSet(to_ref);
- CHECK(!previously_marked_in_bitmap);
}
}
DCHECK(to_ref != nullptr);
@@ -2832,10 +2795,6 @@
DCHECK(heap_->non_moving_space_->HasAddress(to_ref));
DCHECK_EQ(bytes_allocated, non_moving_space_bytes_allocated);
// Free the non-moving-space chunk.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
- CHECK(mark_bitmap != nullptr);
- CHECK(mark_bitmap->Clear(to_ref));
heap_->non_moving_space_->Free(self, to_ref);
}
@@ -2884,6 +2843,14 @@
} else {
DCHECK(heap_->non_moving_space_->HasAddress(to_ref));
DCHECK_EQ(bytes_allocated, non_moving_space_bytes_allocated);
+ if (!kEnableGenerationalConcurrentCopyingCollection || !young_gen_) {
+ // Mark it in the live bitmap.
+ CHECK(!heap_->non_moving_space_->GetLiveBitmap()->AtomicTestAndSet(to_ref));
+ }
+ if (!kUseBakerReadBarrier) {
+ // Mark it in the mark bitmap.
+ CHECK(!heap_->non_moving_space_->GetMarkBitmap()->AtomicTestAndSet(to_ref));
+ }
}
if (kUseBakerReadBarrier) {
DCHECK(to_ref->GetReadBarrierState() == ReadBarrier::GrayState());
@@ -2928,34 +2895,11 @@
to_ref = from_ref;
} else {
// Non-immune non-moving space. Use the mark bitmap.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
- bool is_los = mark_bitmap == nullptr;
- if (!is_los && mark_bitmap->Test(from_ref)) {
+ if (IsMarkedInNonMovingSpace(from_ref)) {
// Already marked.
to_ref = from_ref;
} else {
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
- // We may not have a large object space for dex2oat, don't assume it exists.
- if (los_bitmap == nullptr) {
- CHECK(heap_->GetLargeObjectsSpace() == nullptr)
- << "LOS bitmap covers the entire address range " << from_ref
- << " " << heap_->DumpSpaces();
- }
- if (los_bitmap != nullptr && is_los && los_bitmap->Test(from_ref)) {
- // Already marked in LOS.
- to_ref = from_ref;
- } else {
- // Not marked.
- if (IsOnAllocStack(from_ref)) {
- // If on the allocation stack, it's considered marked.
- to_ref = from_ref;
- } else {
- // Not marked.
- to_ref = nullptr;
- }
- }
+ to_ref = nullptr;
}
}
}
@@ -2977,11 +2921,24 @@
DCHECK(!region_space_->HasAddress(ref)) << ref;
DCHECK(!immune_spaces_.ContainsObject(ref));
// Use the mark bitmap.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(ref);
- bool is_los = mark_bitmap == nullptr;
+ accounting::ContinuousSpaceBitmap* mark_bitmap = heap_->GetNonMovingSpace()->GetMarkBitmap();
+ accounting::LargeObjectBitmap* los_bitmap = nullptr;
+ const bool is_los = !mark_bitmap->HasAddress(ref);
+ if (is_los) {
+ if (!IsAligned<kPageSize>(ref)) {
+ // Ref is a large object that is not aligned, it must be heap
+ // corruption. Remove memory protection and dump data before
+ // AtomicSetReadBarrierState since it will fault if the address is not
+ // valid.
+ region_space_->Unprotect();
+ heap_->GetVerification()->LogHeapCorruption(holder, offset, ref, /* fatal= */ true);
+ }
+ DCHECK(heap_->GetLargeObjectsSpace())
+ << "ref=" << ref
+ << " doesn't belong to non-moving space and large object space doesn't exist";
+ los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ DCHECK(los_bitmap->HasAddress(ref));
+ }
if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
// The sticky-bit CC collector is only compatible with Baker-style read barriers.
DCHECK(kUseBakerReadBarrier);
@@ -2999,12 +2956,6 @@
ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(), ReadBarrier::GrayState())) {
// TODO: We don't actually need to scan this object later, we just need to clear the gray
// bit.
- // Also make sure the object is marked.
- if (is_los) {
- los_bitmap->AtomicTestAndSet(ref);
- } else {
- mark_bitmap->AtomicTestAndSet(ref);
- }
// We don't need to mark newly allocated objects (those in allocation stack) as they can
// only point to to-space objects. Also, they are considered live till the next GC cycle.
PushOntoMarkStack(self, ref);
@@ -3016,65 +2967,34 @@
// Already marked.
} else if (is_los && los_bitmap->Test(ref)) {
// Already marked in LOS.
- } else {
- // Not marked.
- if (IsOnAllocStack(ref)) {
- // If it's on the allocation stack, it's considered marked. Keep it white (non-gray).
- // Objects on the allocation stack need not be marked.
- if (!is_los) {
- DCHECK(!mark_bitmap->Test(ref));
- } else {
- DCHECK(!los_bitmap->Test(ref));
- }
- if (kUseBakerReadBarrier) {
- DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::NonGrayState());
- }
+ } else if (IsOnAllocStack(ref)) {
+ // If it's on the allocation stack, it's considered marked. Keep it white (non-gray).
+ // Objects on the allocation stack need not be marked.
+ if (!is_los) {
+ DCHECK(!mark_bitmap->Test(ref));
} else {
- // For the baker-style RB, we need to handle 'false-gray' cases. See the
- // kRegionTypeUnevacFromSpace-case comment in Mark().
+ DCHECK(!los_bitmap->Test(ref));
+ }
+ if (kUseBakerReadBarrier) {
+ DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::NonGrayState());
+ }
+ } else {
+ // Not marked nor on the allocation stack. Try to mark it.
+ // This may or may not succeed, which is ok.
+ bool success = false;
+ if (kUseBakerReadBarrier) {
+ success = ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(),
+ ReadBarrier::GrayState());
+ } else {
+ success = is_los ?
+ !los_bitmap->AtomicTestAndSet(ref) :
+ !mark_bitmap->AtomicTestAndSet(ref);
+ }
+ if (success) {
if (kUseBakerReadBarrier) {
- // Test the bitmap first to reduce the chance of false gray cases.
- if ((!is_los && mark_bitmap->Test(ref)) ||
- (is_los && los_bitmap->Test(ref))) {
- return ref;
- }
+ DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
}
- if (is_los && !IsAligned<kPageSize>(ref)) {
- // Ref is a large object that is not aligned, it must be heap
- // corruption. Remove memory protection and dump data before
- // AtomicSetReadBarrierState since it will fault if the address is not
- // valid.
- region_space_->Unprotect();
- heap_->GetVerification()->LogHeapCorruption(holder, offset, ref, /* fatal= */ true);
- }
- // Not marked nor on the allocation stack. Try to mark it.
- // This may or may not succeed, which is ok.
- bool cas_success = false;
- if (kUseBakerReadBarrier) {
- cas_success = ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(),
- ReadBarrier::GrayState());
- }
- if (!is_los && mark_bitmap->AtomicTestAndSet(ref)) {
- // Already marked.
- if (kUseBakerReadBarrier &&
- cas_success &&
- ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
- PushOntoFalseGrayStack(self, ref);
- }
- } else if (is_los && los_bitmap->AtomicTestAndSet(ref)) {
- // Already marked in LOS.
- if (kUseBakerReadBarrier &&
- cas_success &&
- ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
- PushOntoFalseGrayStack(self, ref);
- }
- } else {
- // Newly marked.
- if (kUseBakerReadBarrier) {
- DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
- }
- PushOntoMarkStack(self, ref);
- }
+ PushOntoMarkStack(self, ref);
}
}
return ref;
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 6535b11..237e070 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -218,6 +218,8 @@
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
bool IsMarkedInUnevacFromSpace(mirror::Object* from_ref)
REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsMarkedInNonMovingSpace(mirror::Object* from_ref)
+ REQUIRES_SHARED(Locks::mutator_lock_);
bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* field,
bool do_atomic_update) override
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -283,11 +285,6 @@
ALWAYS_INLINE mirror::Object* MarkImmuneSpace(Thread* const self,
mirror::Object* from_ref)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!immune_gray_stack_lock_);
- void PushOntoFalseGrayStack(Thread* const self, mirror::Object* obj)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!mark_stack_lock_);
- void ProcessFalseGrayStack() REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!mark_stack_lock_);
void ScanImmuneObject(mirror::Object* obj)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
mirror::Object* MarkFromReadBarrierWithMeasurements(Thread* const self,
@@ -315,7 +312,6 @@
// (see use case in ConcurrentCopying::MarkFromReadBarrier).
bool rb_mark_bit_stack_full_;
- std::vector<mirror::Object*> false_gray_stack_ GUARDED_BY(mark_stack_lock_);
Mutex mark_stack_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::vector<accounting::ObjectStack*> revoked_mark_stacks_
GUARDED_BY(mark_stack_lock_);
diff --git a/runtime/gc/collector/immune_region.h b/runtime/gc/collector/immune_region.h
index c9ac435..80ee44c 100644
--- a/runtime/gc/collector/immune_region.h
+++ b/runtime/gc/collector/immune_region.h
@@ -18,7 +18,6 @@
#define ART_RUNTIME_GC_COLLECTOR_IMMUNE_REGION_H_
#include "base/macros.h"
-#include "base/mutex.h"
namespace art {
namespace mirror {
diff --git a/runtime/gc/collector/immune_spaces.h b/runtime/gc/collector/immune_spaces.h
index 72cb60d..5a8441a 100644
--- a/runtime/gc/collector/immune_spaces.h
+++ b/runtime/gc/collector/immune_spaces.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_GC_COLLECTOR_IMMUNE_SPACES_H_
#define ART_RUNTIME_GC_COLLECTOR_IMMUNE_SPACES_H_
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "gc/space/space.h"
#include "immune_region.h"
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index bb42be6..6fab371 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -20,8 +20,8 @@
#include <memory>
#include "base/atomic.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "garbage_collector.h"
#include "gc/accounting/heap_bitmap.h"
#include "gc_root.h"
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index d4af117..4944639 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -17,6 +17,7 @@
#include "reference_processor.h"
#include "art_field-inl.h"
+#include "base/mutex.h"
#include "base/time_utils.h"
#include "base/utils.h"
#include "class_root.h"
diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h
index c6c7836..17b546a 100644
--- a/runtime/gc/reference_processor.h
+++ b/runtime/gc/reference_processor.h
@@ -18,7 +18,7 @@
#define ART_RUNTIME_GC_REFERENCE_PROCESSOR_H_
#include "base/globals.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "jni.h"
#include "reference_queue.h"
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index 5c11e50..95871da 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -17,6 +17,7 @@
#include "reference_queue.h"
#include "accounting/card_table-inl.h"
+#include "base/mutex.h"
#include "collector/concurrent_copying.h"
#include "heap.h"
#include "mirror/class-inl.h"
diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h
index 09ab51a..53518cc 100644
--- a/runtime/gc/reference_queue.h
+++ b/runtime/gc/reference_queue.h
@@ -23,7 +23,7 @@
#include "base/atomic.h"
#include "base/globals.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "base/timing_logger.h"
#include "jni.h"
#include "obj_ptr.h"
@@ -31,6 +31,9 @@
#include "thread_pool.h"
namespace art {
+
+class Mutex;
+
namespace mirror {
class Reference;
} // namespace mirror
diff --git a/runtime/gc/scoped_gc_critical_section.h b/runtime/gc/scoped_gc_critical_section.h
index 864bf87..8ad0158 100644
--- a/runtime/gc/scoped_gc_critical_section.h
+++ b/runtime/gc/scoped_gc_critical_section.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_GC_SCOPED_GC_CRITICAL_SECTION_H_
#define ART_RUNTIME_GC_SCOPED_GC_CRITICAL_SECTION_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "collector_type.h"
#include "gc_cause.h"
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index 383bf7a..6d9fd04 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -19,6 +19,8 @@
#include "space.h"
+#include "base/mutex.h"
+
namespace art {
namespace mirror {
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 5dd8136..7d28516 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -21,6 +21,7 @@
#include <ostream>
#include "base/memory_tool.h"
+#include "base/mutex.h"
namespace art {
namespace gc {
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 545e3d8..dd5451b 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -22,9 +22,9 @@
#include "base/atomic.h"
#include "base/globals.h"
+#include "base/locks.h"
#include "base/macros.h"
#include "base/mem_map.h"
-#include "base/mutex.h"
#include "gc/accounting/space_bitmap.h"
#include "gc/collector/object_byte_pair.h"
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index 0bd43f9..32af62d 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_GC_ROOT_H_
#define ART_RUNTIME_GC_ROOT_H_
+#include "base/locks.h" // For Locks::mutator_lock_.
#include "base/macros.h"
-#include "base/mutex.h" // For Locks::mutator_lock_.
#include "mirror/object_reference.h"
#include "read_barrier_option.h"
diff --git a/runtime/handle.h b/runtime/handle.h
index b13c43e..0c9c029 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -20,8 +20,8 @@
#include <android-base/logging.h>
#include "base/casts.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "base/value_object.h"
#include "jni.h"
#include "obj_ptr.h"
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 9eaf1ec..1a1c92f 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -22,8 +22,8 @@
#include <android-base/logging.h>
#include "base/enums.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "handle.h"
#include "stack_reference.h"
#include "verify_object.h"
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 614154c..d9d81f0 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -20,7 +20,7 @@
#include "art_field.h"
#include "art_method.h"
#include "base/hiddenapi_flags.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "intrinsics_enum.h"
#include "mirror/class-inl.h"
#include "reflection.h"
diff --git a/runtime/imtable.h b/runtime/imtable.h
index 3c52fb8..48a8643 100644
--- a/runtime/imtable.h
+++ b/runtime/imtable.h
@@ -23,8 +23,8 @@
#include "base/casts.h"
#include "base/enums.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
namespace art {
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index 8c63c00..eb07035 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -26,9 +26,9 @@
#include <android-base/logging.h>
#include "base/bit_utils.h"
+#include "base/locks.h"
#include "base/macros.h"
#include "base/mem_map.h"
-#include "base/mutex.h"
#include "gc_root.h"
#include "obj_ptr.h"
#include "offsets.h"
diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h
index d7e69a6..e92d195 100644
--- a/runtime/interpreter/interpreter.h
+++ b/runtime/interpreter/interpreter.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_H_
#define ART_RUNTIME_INTERPRETER_INTERPRETER_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "dex/dex_file.h"
#include "obj_ptr.h"
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index bf84227..a633a63 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -32,8 +32,8 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/enums.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "class_linker-inl.h"
#include "class_root.h"
#include "common_dex_operations.h"
diff --git a/runtime/interpreter/interpreter_mterp_impl.h b/runtime/interpreter/interpreter_mterp_impl.h
index d8a764f..177b0fd 100644
--- a/runtime/interpreter/interpreter_mterp_impl.h
+++ b/runtime/interpreter/interpreter_mterp_impl.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_MTERP_IMPL_H_
#define ART_RUNTIME_INTERPRETER_INTERPRETER_MTERP_IMPL_H_
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "dex/dex_file.h"
#include "jvalue.h"
#include "obj_ptr.h"
diff --git a/runtime/interpreter/interpreter_switch_impl.h b/runtime/interpreter/interpreter_switch_impl.h
index 9fc4239..d4dca11 100644
--- a/runtime/interpreter/interpreter_switch_impl.h
+++ b/runtime/interpreter/interpreter_switch_impl.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_SWITCH_IMPL_H_
#define ART_RUNTIME_INTERPRETER_INTERPRETER_SWITCH_IMPL_H_
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "dex/dex_file.h"
#include "dex/code_item_accessors.h"
#include "jvalue.h"
diff --git a/runtime/interpreter/lock_count_data.h b/runtime/interpreter/lock_count_data.h
index 3098d4f..efa14c5 100644
--- a/runtime/interpreter/lock_count_data.h
+++ b/runtime/interpreter/lock_count_data.h
@@ -20,7 +20,7 @@
#include <memory>
#include <vector>
-#include "base/mutex.h"
+#include "base/locks.h"
namespace art {
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 6609021..ca98999 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -21,8 +21,8 @@
#include <cstring>
#include <string>
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "dex/dex_file.h"
#include "lock_count_data.h"
#include "read_barrier.h"
diff --git a/runtime/java_frame_root_info.h b/runtime/java_frame_root_info.h
index 452a76b..8141ea2 100644
--- a/runtime/java_frame_root_info.h
+++ b/runtime/java_frame_root_info.h
@@ -19,8 +19,8 @@
#include <iosfwd>
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "gc_root.h"
namespace art {
diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h
index 3d25910..fb5e81b 100644
--- a/runtime/jit/debugger_interface.h
+++ b/runtime/jit/debugger_interface.h
@@ -22,7 +22,7 @@
#include <vector>
#include "base/array_ref.h"
-#include "base/mutex.h"
+#include "base/locks.h"
namespace art {
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 4a3ef07..e43d771 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -291,22 +291,6 @@
return success;
}
-void Jit::CreateThreadPool() {
- if (Runtime::Current()->IsSafeMode()) {
- // Never create the pool in safe mode.
- return;
- }
- // There is a DCHECK in the 'AddSamples' method to ensure the thread pool
- // is not null when we instrument.
-
- // We need peers as we may report the JIT thread, e.g., in the debugger.
- constexpr bool kJitPoolNeedsPeers = true;
- thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers));
-
- thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority());
- Start();
-}
-
void Jit::DeleteThreadPool() {
Thread* self = Thread::Current();
DCHECK(Runtime::Current()->IsShuttingDown(self));
@@ -562,10 +546,10 @@
class JitCompileTask final : public Task {
public:
- enum TaskKind {
+ enum class TaskKind {
kAllocateProfile,
kCompile,
- kCompileOsr
+ kCompileOsr,
};
JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
@@ -582,14 +566,20 @@
void Run(Thread* self) override {
ScopedObjectAccess soa(self);
- if (kind_ == kCompile) {
- Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr= */ false);
- } else if (kind_ == kCompileOsr) {
- Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr= */ true);
- } else {
- DCHECK(kind_ == kAllocateProfile);
- if (ProfilingInfo::Create(self, method_, /* retry_allocation= */ true)) {
- VLOG(jit) << "Start profiling " << ArtMethod::PrettyMethod(method_);
+ switch (kind_) {
+ case TaskKind::kCompile:
+ case TaskKind::kCompileOsr: {
+ Runtime::Current()->GetJit()->CompileMethod(
+ method_,
+ self,
+ /* osr= */ (kind_ == TaskKind::kCompileOsr));
+ break;
+ }
+ case TaskKind::kAllocateProfile: {
+ if (ProfilingInfo::Create(self, method_, /* retry_allocation= */ true)) {
+ VLOG(jit) << "Start profiling " << ArtMethod::PrettyMethod(method_);
+ }
+ break;
}
}
ProfileSaver::NotifyJitActivity();
@@ -607,6 +597,18 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
};
+void Jit::CreateThreadPool() {
+ // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
+ // is not null when we instrument.
+
+ // We need peers as we may report the JIT thread, e.g., in the debugger.
+ constexpr bool kJitPoolNeedsPeers = true;
+ thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers));
+
+ thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority());
+ Start();
+}
+
static bool IgnoreSamplesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
if (method->IsClassInitializer() || !method->IsCompilable()) {
// We do not want to compile such methods.
@@ -630,11 +632,10 @@
void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) {
if (thread_pool_ == nullptr) {
- // Should only see this when shutting down, starting up, or in zygote, which doesn't
- // have a thread pool.
+ // Should only see this when shutting down, starting up, or in safe mode.
DCHECK(Runtime::Current()->IsShuttingDown(self) ||
!Runtime::Current()->IsFinishedStarting() ||
- Runtime::Current()->IsZygote());
+ Runtime::Current()->IsSafeMode());
return;
}
if (IgnoreSamplesForMethod(method)) {
@@ -675,7 +676,8 @@
if (!success) {
// We failed allocating. Instead of doing the collection on the Java thread, we push
// an allocation to a compiler thread, that will do the collection.
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
+ thread_pool_->AddTask(
+ self, new JitCompileTask(method, JitCompileTask::TaskKind::kAllocateProfile));
}
}
// Avoid jumping more than one state at a time.
@@ -685,7 +687,7 @@
if ((new_count >= HotMethodThreshold()) &&
!code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::TaskKind::kCompile));
}
// Avoid jumping more than one state at a time.
new_count = std::min(new_count, static_cast<uint32_t>(OSRMethodThreshold() - 1));
@@ -697,7 +699,8 @@
DCHECK(!method->IsNative()); // No back edges reported for native methods.
if ((new_count >= OSRMethodThreshold()) && !code_cache_->IsOsrCompiled(method)) {
DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+ thread_pool_->AddTask(
+ self, new JitCompileTask(method, JitCompileTask::TaskKind::kCompileOsr));
}
}
}
@@ -730,7 +733,7 @@
// The compiler requires a ProfilingInfo object for non-native methods.
ProfilingInfo::Create(thread, np_method, /* retry_allocation= */ true);
}
- JitCompileTask compile_task(method, JitCompileTask::kCompile);
+ JitCompileTask compile_task(method, JitCompileTask::TaskKind::kCompile);
// Fake being in a runtime thread so that class-load behavior will be the same as normal jit.
ScopedSetRuntimeThread ssrt(thread);
compile_task.Run(thread);
@@ -798,7 +801,16 @@
}
}
-void Jit::PostForkChildAction() {
+void Jit::PostForkChildAction(bool is_zygote) {
+ if (is_zygote) {
+ // Don't transition if this is for a child zygote.
+ return;
+ }
+ if (Runtime::Current()->IsSafeMode()) {
+ // Delete the thread pool, we are not going to JIT.
+ thread_pool_.reset(nullptr);
+ return;
+ }
// At this point, the compiler options have been adjusted to the particular configuration
// of the forked child. Parse them again.
jit_update_options_(jit_compiler_handle_);
@@ -806,6 +818,28 @@
// Adjust the status of code cache collection: the status from zygote was to not collect.
code_cache_->SetGarbageCollectCode(!jit_generate_debug_info_(jit_compiler_handle_) &&
!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled());
+
+ if (thread_pool_ != nullptr) {
+ // Remove potential tasks that have been inherited from the zygote.
+ thread_pool_->RemoveAllTasks(Thread::Current());
+
+ // Resume JIT compilation.
+ thread_pool_->CreateThreads();
+ }
+}
+
+void Jit::PreZygoteFork() {
+ if (thread_pool_ == nullptr) {
+ return;
+ }
+ thread_pool_->DeleteThreads();
+}
+
+void Jit::PostZygoteFork() {
+ if (thread_pool_ == nullptr) {
+ return;
+ }
+ thread_pool_->CreateThreads();
}
} // namespace jit
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index e12b032..7ce5f07 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -285,8 +285,14 @@
// Start JIT threads.
void Start();
- // Transition to a zygote child state.
- void PostForkChildAction();
+ // Transition to a child state.
+ void PostForkChildAction(bool is_zygote);
+
+ // Prepare for forking.
+ void PreZygoteFork();
+
+ // Adjust state after forking.
+ void PostZygoteFork();
private:
Jit(JitCodeCache* code_cache, JitOptions* options);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 97887cc..1d53a58 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -436,6 +436,12 @@
initial_capacity = RoundDown(initial_capacity, 2 * kPageSize);
max_capacity = RoundDown(max_capacity, 2 * kPageSize);
+ used_memory_for_data_ = 0;
+ used_memory_for_code_ = 0;
+ number_of_compilations_ = 0;
+ number_of_osr_compilations_ = 0;
+ number_of_collections_ = 0;
+
data_pages_ = MemMap();
exec_pages_ = MemMap();
non_exec_pages_ = MemMap();
@@ -477,7 +483,7 @@
JitCodeCache::~JitCodeCache() {}
bool JitCodeCache::ContainsPc(const void* ptr) const {
- return exec_pages_.Begin() <= ptr && ptr < exec_pages_.End();
+ return exec_pages_.HasAddress(ptr) || zygote_exec_pages_.HasAddress(ptr);
}
bool JitCodeCache::WillExecuteJitCode(ArtMethod* method) {
@@ -1321,7 +1327,7 @@
return true;
}
const void* code = method_header->GetCode();
- if (code_cache_->ContainsPc(code)) {
+ if (code_cache_->ContainsPc(code) && !code_cache_->IsInZygoteExecSpace(code)) {
// Use the atomic set version, as multiple threads are executing this code.
bitmap_->AtomicTestAndSet(FromCodeToAllocation(code));
}
@@ -1493,7 +1499,7 @@
// interpreter will update its entry point to the compiled code and call it.
for (ProfilingInfo* info : profiling_infos_) {
const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
- if (ContainsPc(entry_point)) {
+ if (!IsInZygoteDataSpace(info) && ContainsPc(entry_point)) {
info->SetSavedEntryPoint(entry_point);
// Don't call Instrumentation::UpdateMethodsCode(), as it can check the declaring
// class of the method. We may be concurrently running a GC which makes accessing
@@ -1508,7 +1514,7 @@
// Change entry points of native methods back to the GenericJNI entrypoint.
for (const auto& entry : jni_stubs_map_) {
const JniStubData& data = entry.second;
- if (!data.IsCompiled()) {
+ if (!data.IsCompiled() || IsInZygoteExecSpace(data.GetCode())) {
continue;
}
// Make sure a single invocation of the GenericJNI trampoline tries to recompile.
@@ -1540,7 +1546,9 @@
// Iterate over all compiled code and remove entries that are not marked.
for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) {
JniStubData* data = &it->second;
- if (!data->IsCompiled() || GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) {
+ if (IsInZygoteExecSpace(data->GetCode()) ||
+ !data->IsCompiled() ||
+ GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) {
++it;
} else {
method_headers.insert(OatQuickMethodHeader::FromCodePointer(data->GetCode()));
@@ -1550,7 +1558,7 @@
for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
const void* code_ptr = it->first;
uintptr_t allocation = FromCodeToAllocation(code_ptr);
- if (GetLiveBitmap()->Test(allocation)) {
+ if (IsInZygoteExecSpace(code_ptr) || GetLiveBitmap()->Test(allocation)) {
++it;
} else {
OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(code_ptr);
@@ -1571,7 +1579,7 @@
// Also remove the saved entry point from the ProfilingInfo objects.
for (ProfilingInfo* info : profiling_infos_) {
const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
- if (!ContainsPc(ptr) && !info->IsInUseByCompiler()) {
+ if (!ContainsPc(ptr) && !info->IsInUseByCompiler() && !IsInZygoteDataSpace(info)) {
info->GetMethod()->SetProfilingInfo(nullptr);
}
@@ -1596,6 +1604,9 @@
for (const auto& entry : jni_stubs_map_) {
const JniStubData& data = entry.second;
const void* code_ptr = data.GetCode();
+ if (IsInZygoteExecSpace(code_ptr)) {
+ continue;
+ }
const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
for (ArtMethod* method : data.GetMethods()) {
if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) {
@@ -1607,6 +1618,9 @@
for (const auto& it : method_code_map_) {
ArtMethod* method = it.second;
const void* code_ptr = it.first;
+ if (IsInZygoteExecSpace(code_ptr)) {
+ continue;
+ }
const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) {
GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr));
@@ -1953,6 +1967,7 @@
instrumentation->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
}
if (collection_in_progress_) {
+ CHECK(!IsInZygoteExecSpace(data->GetCode()));
GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode()));
}
}
@@ -2057,6 +2072,7 @@
}
void JitCodeCache::FreeCode(uint8_t* code) {
+ CHECK(!IsInZygoteExecSpace(code));
used_memory_for_code_ -= mspace_usable_size(code);
mspace_free(exec_mspace_, code);
}
@@ -2068,6 +2084,7 @@
}
void JitCodeCache::FreeData(uint8_t* data) {
+ CHECK(!IsInZygoteDataSpace(data));
used_memory_for_data_ -= mspace_usable_size(data);
mspace_free(data_mspace_, data);
}
@@ -2091,13 +2108,11 @@
}
void JitCodeCache::PostForkChildAction(bool is_system_server, bool is_zygote) {
+ if (is_zygote) {
+ // Don't transition if this is for a child zygote.
+ return;
+ }
MutexLock mu(Thread::Current(), lock_);
- // Currently, we don't expect any compilations from zygote.
- CHECK_EQ(number_of_compilations_, 0u);
- CHECK_EQ(number_of_osr_compilations_, 0u);
- CHECK(jni_stubs_map_.empty());
- CHECK(method_code_map_.empty());
- CHECK(osr_code_map_.empty());
zygote_data_pages_ = std::move(data_pages_);
zygote_exec_pages_ = std::move(exec_pages_);
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 7a838fd..e2f3357 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -71,6 +71,7 @@
namespace jit {
+class MarkCodeClosure;
class ScopedCodeCacheWrite;
// Alignment in bits that will suit all architectures.
@@ -387,6 +388,14 @@
const MemMap* GetUpdatableCodeMapping() const;
+ bool IsInZygoteDataSpace(const void* ptr) const {
+ return zygote_data_pages_.HasAddress(ptr);
+ }
+
+ bool IsInZygoteExecSpace(const void* ptr) const {
+ return zygote_exec_pages_.HasAddress(ptr);
+ }
+
bool IsWeakAccessEnabled(Thread* self) const;
void WaitUntilInlineCacheAccessible(Thread* self)
REQUIRES(!lock_)
@@ -487,6 +496,7 @@
friend class art::JitJniStubTestHelper;
friend class ScopedCodeCacheWrite;
+ friend class MarkCodeClosure;
DISALLOW_COPY_AND_ASSIGN(JitCodeCache);
};
diff --git a/runtime/jni/jni_env_ext.cc b/runtime/jni/jni_env_ext.cc
index efe43ee..976f89b 100644
--- a/runtime/jni/jni_env_ext.cc
+++ b/runtime/jni/jni_env_ext.cc
@@ -21,6 +21,7 @@
#include "android-base/stringprintf.h"
+#include "base/mutex.h"
#include "base/to_str.h"
#include "check_jni.h"
#include "indirect_reference_table.h"
diff --git a/runtime/jni/jni_env_ext.h b/runtime/jni/jni_env_ext.h
index 3a007ad..61de074 100644
--- a/runtime/jni/jni_env_ext.h
+++ b/runtime/jni/jni_env_ext.h
@@ -19,8 +19,8 @@
#include <jni.h>
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "indirect_reference_table.h"
#include "obj_ptr.h"
#include "reference_table.h"
diff --git a/runtime/jvalue.h b/runtime/jvalue.h
index b42d995..d03749c 100644
--- a/runtime/jvalue.h
+++ b/runtime/jvalue.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_JVALUE_H_
#define ART_RUNTIME_JVALUE_H_
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include <stdint.h>
diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h
index 6a0f075..3fb83ac 100644
--- a/runtime/managed_stack.h
+++ b/runtime/managed_stack.h
@@ -23,8 +23,8 @@
#include <android-base/logging.h>
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "base/bit_utils.h"
namespace art {
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index e3cb12f..783ba6a 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_MIRROR_CLASS_LOADER_H_
#define ART_RUNTIME_MIRROR_CLASS_LOADER_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "obj_ptr.h"
#include "object.h"
#include "object_reference.h"
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 58b199d..c742928 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -19,7 +19,7 @@
#include "array.h"
#include "base/bit_utils.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "dex/dex_file_types.h"
#include "gc_root.h" // Note: must not use -inl here to avoid circular dependency.
#include "object.h"
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index d6a39aa..8636928 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -19,7 +19,7 @@
#include "base/atomic.h"
#include "base/globals.h"
-#include "base/mutex.h" // For Locks::mutator_lock_.
+#include "base/locks.h" // For Locks::mutator_lock_.
#include "heap_poisoning.h"
#include "obj_ptr.h"
diff --git a/runtime/mirror/reference.h b/runtime/mirror/reference.h
index 63c5ae5..9ace4f7 100644
--- a/runtime/mirror/reference.h
+++ b/runtime/mirror/reference.h
@@ -18,8 +18,8 @@
#define ART_RUNTIME_MIRROR_REFERENCE_H_
#include "base/enums.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "obj_ptr.h"
#include "object.h"
#include "read_barrier_option.h"
diff --git a/runtime/monitor_objects_stack_visitor.h b/runtime/monitor_objects_stack_visitor.h
index c943402..3968239 100644
--- a/runtime/monitor_objects_stack_visitor.h
+++ b/runtime/monitor_objects_stack_visitor.h
@@ -20,7 +20,7 @@
#include <android-base/logging.h>
#include "art_method.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "monitor.h"
#include "stack.h"
#include "thread.h"
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 0f655b9..b7ac1e8 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -249,6 +249,13 @@
return reinterpret_cast<jlong>(ThreadForEnv(env));
}
+static void ZygoteHooks_nativePostZygoteFork(JNIEnv*, jclass) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->IsZygote()) {
+ runtime->PostZygoteFork();
+ }
+}
+
static void ZygoteHooks_nativePostForkSystemServer(JNIEnv* env ATTRIBUTE_UNUSED,
jclass klass ATTRIBUTE_UNUSED) {
// This JIT code cache for system server is created whilst the runtime is still single threaded.
@@ -305,7 +312,7 @@
/* is_system_server= */ false, is_zygote);
}
// This must be called after EnableDebugFeatures.
- Runtime::Current()->GetJit()->PostForkChildAction();
+ Runtime::Current()->GetJit()->PostForkChildAction(is_zygote);
}
// Update tracing.
@@ -403,6 +410,7 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ZygoteHooks, nativePreFork, "()J"),
+ NATIVE_METHOD(ZygoteHooks, nativePostZygoteFork, "()V"),
NATIVE_METHOD(ZygoteHooks, nativePostForkSystemServer, "()V"),
NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JIZZLjava/lang/String;)V"),
NATIVE_METHOD(ZygoteHooks, startZygoteNoThreadCreation, "()V"),
diff --git a/runtime/non_debuggable_classes.h b/runtime/non_debuggable_classes.h
index e1b5633..e2c51e6 100644
--- a/runtime/non_debuggable_classes.h
+++ b/runtime/non_debuggable_classes.h
@@ -19,7 +19,7 @@
#include <vector>
-#include "base/mutex.h"
+#include "base/locks.h"
#include "jni.h"
namespace art {
diff --git a/runtime/nth_caller_visitor.h b/runtime/nth_caller_visitor.h
index 71c6a82..ffec179 100644
--- a/runtime/nth_caller_visitor.h
+++ b/runtime/nth_caller_visitor.h
@@ -18,7 +18,7 @@
#define ART_RUNTIME_NTH_CALLER_VISITOR_H_
#include "art_method.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "stack.h"
namespace art {
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 2c882ec..9552ca3 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -27,6 +27,7 @@
#include "base/bit_vector-inl.h"
#include "base/file_utils.h"
#include "base/logging.h" // For VLOG.
+#include "base/mutex-inl.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "class_linker.h"
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 7d96a7a..99e1b73 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -23,8 +23,8 @@
#include <unordered_map>
#include <vector>
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "jni.h"
namespace art {
diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h
index efbb66f..9e2ee29 100644
--- a/runtime/obj_ptr.h
+++ b/runtime/obj_ptr.h
@@ -21,8 +21,8 @@
#include <type_traits>
#include "base/globals.h"
+#include "base/locks.h" // For Locks::mutator_lock_.
#include "base/macros.h"
-#include "base/mutex.h" // For Locks::mutator_lock_.
// Always inline ObjPtr methods even in debug builds.
#define OBJPTR_INLINE __attribute__ ((always_inline))
diff --git a/runtime/object_lock.h b/runtime/object_lock.h
index 5916f90..15b763a 100644
--- a/runtime/object_lock.h
+++ b/runtime/object_lock.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_OBJECT_LOCK_H_
#define ART_RUNTIME_OBJECT_LOCK_H_
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "handle.h"
namespace art {
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index 0741da6..3b89377 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -19,8 +19,8 @@
#include <android-base/logging.h>
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "base/runtime_debug.h"
#include "gc_root.h"
#include "jni.h"
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index 6af5ca5..6388944 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -23,7 +23,7 @@
#include <vector>
#include "base/allocator.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "gc_root.h"
#include "obj_ptr.h"
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 74580a2..574e302 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_REFLECTION_H_
#define ART_RUNTIME_REFLECTION_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "dex/primitive.h"
#include "jni.h"
#include "obj_ptr.h"
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index e6cc471..2ffaf98 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -23,6 +23,7 @@
#include "art_method.h"
#include "base/callee_save_type.h"
#include "base/casts.h"
+#include "base/mutex.h"
#include "entrypoints/quick/callee_save_frame.h"
#include "gc_root-inl.h"
#include "interpreter/mterp/mterp.h"
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 84526f3..8e3d71a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -234,8 +234,7 @@
class_linker_(nullptr),
signal_catcher_(nullptr),
java_vm_(nullptr),
- fault_message_lock_("Fault message lock"),
- fault_message_(""),
+ fault_message_(nullptr),
threads_being_born_(0),
shutdown_cond_(new ConditionVariable("Runtime shutdown", *Locks::runtime_shutdown_lock_)),
shutting_down_(false),
@@ -601,9 +600,18 @@
}
void Runtime::PreZygoteFork() {
+ if (GetJit() != nullptr) {
+ GetJit()->PreZygoteFork();
+ }
heap_->PreZygoteFork();
}
+void Runtime::PostZygoteFork() {
+ if (GetJit() != nullptr) {
+ GetJit()->PostZygoteFork();
+ }
+}
+
void Runtime::CallExitHook(jint status) {
if (exit_ != nullptr) {
ScopedThreadStateChange tsc(Thread::Current(), kNative);
@@ -917,10 +925,6 @@
}
}
- if (jit_ != nullptr) {
- jit_->CreateThreadPool();
- }
-
if (thread_pool_ == nullptr) {
constexpr size_t kStackSize = 64 * KB;
constexpr size_t kMaxRuntimeWorkers = 4u;
@@ -2367,8 +2371,27 @@
}
void Runtime::SetFaultMessage(const std::string& message) {
- MutexLock mu(Thread::Current(), fault_message_lock_);
- fault_message_ = message;
+ std::string* new_msg = new std::string(message);
+ std::string* cur_msg = fault_message_.exchange(new_msg);
+ delete cur_msg;
+}
+
+std::string Runtime::GetFaultMessage() {
+ // Retrieve the message. Temporarily replace with null so that SetFaultMessage will not delete
+ // the string in parallel.
+ std::string* cur_msg = fault_message_.exchange(nullptr);
+
+ // Make a copy of the string.
+ std::string ret = cur_msg == nullptr ? "" : *cur_msg;
+
+ // Put the message back if it hasn't been updated.
+ std::string* null_str = nullptr;
+ if (!fault_message_.compare_exchange_strong(null_str, cur_msg)) {
+ // Already replaced.
+ delete cur_msg;
+ }
+
+ return ret;
}
void Runtime::AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::string>* argv)
@@ -2430,6 +2453,8 @@
LOG(WARNING) << "Failed to allocate JIT";
// Release JIT code cache resources (several MB of memory).
jit_code_cache_.reset();
+ } else {
+ jit->CreateThreadPool();
}
}
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c74647e..4533376 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -28,9 +28,9 @@
#include <vector>
#include "arch/instruction_set.h"
+#include "base/locks.h"
#include "base/macros.h"
#include "base/mem_map.h"
-#include "base/mutex.h"
#include "deoptimization_kind.h"
#include "dex/dex_file_types.h"
#include "experimental_flags.h"
@@ -445,6 +445,7 @@
bool UseJitCompilation() const;
void PreZygoteFork();
+ void PostZygoteFork();
void InitNonZygoteOrPostFork(
JNIEnv* env,
bool is_system_server,
@@ -511,12 +512,7 @@
void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void SetFaultMessage(const std::string& message) REQUIRES(!fault_message_lock_);
- // Only read by the signal handler, NO_THREAD_SAFETY_ANALYSIS to prevent lock order violations
- // with the unexpected_signal_lock_.
- const std::string& GetFaultMessage() NO_THREAD_SAFETY_ANALYSIS {
- return fault_message_;
- }
+ void SetFaultMessage(const std::string& message);
void AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::string>* arg_vector) const;
@@ -824,6 +820,12 @@
void VisitConstantRoots(RootVisitor* visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Note: To be lock-free, GetFaultMessage temporarily replaces the lock message with null.
+ // As such, there is a window where a call will return an empty string. In general,
+ // only aborting code should retrieve this data (via GetFaultMessageForAbortLogging
+ // friend).
+ std::string GetFaultMessage();
+
// A pointer to the active runtime or null.
static Runtime* instance_;
@@ -909,9 +911,9 @@
std::unique_ptr<jit::JitCodeCache> jit_code_cache_;
std::unique_ptr<jit::JitOptions> jit_options_;
- // Fault message, printed when we get a SIGSEGV.
- Mutex fault_message_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::string fault_message_ GUARDED_BY(fault_message_lock_);
+ // Fault message, printed when we get a SIGSEGV. Stored as a native-heap object and accessed
+ // lock-free, so needs to be atomic.
+ std::atomic<std::string*> fault_message_;
// A non-zero value indicates that a thread has been created but not yet initialized. Guarded by
// the shutdown lock so that threads aren't born while we're shutting down.
@@ -1111,6 +1113,9 @@
uint32_t verifier_logging_threshold_ms_;
+ // Note: See comments on GetFaultMessage.
+ friend std::string GetFaultMessageForAbortLogging();
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 4cce15e..32ee3aa3 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -20,8 +20,8 @@
#include <vector>
#include "base/array_ref.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "dex/dex_file.h"
#include "handle.h"
diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc
index eae2505..5676577 100644
--- a/runtime/runtime_common.cc
+++ b/runtime/runtime_common.cc
@@ -371,6 +371,11 @@
#pragma GCC diagnostic ignored "-Wframe-larger-than="
#endif
+std::string GetFaultMessageForAbortLogging() {
+ Runtime* runtime = Runtime::Current();
+ return (runtime != nullptr) ? runtime->GetFaultMessage() : "";
+}
+
static void HandleUnexpectedSignalCommonDump(int signal_number,
siginfo_t* info,
void* raw_context,
@@ -427,9 +432,9 @@
}
if (dump_on_stderr) {
- std::cerr << "Fault message: " << runtime->GetFaultMessage() << std::endl;
+ std::cerr << "Fault message: " << GetFaultMessageForAbortLogging() << std::endl;
} else {
- LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage();
+ LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << GetFaultMessageForAbortLogging();
}
}
}
diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h
index 3089c24..2541ab5 100644
--- a/runtime/scoped_thread_state_change-inl.h
+++ b/runtime/scoped_thread_state_change-inl.h
@@ -22,6 +22,7 @@
#include <android-base/logging.h>
#include "base/casts.h"
+#include "base/mutex.h"
#include "jni/jni_env_ext-inl.h"
#include "obj_ptr-inl.h"
#include "runtime.h"
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 0c42c5a..b2ad90a 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -19,8 +19,8 @@
#include "jni.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "base/value_object.h"
#include "thread_state.h"
diff --git a/runtime/stack.h b/runtime/stack.h
index 9d30115..0edf4f5 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -20,8 +20,8 @@
#include <stdint.h>
#include <string>
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "quick/quick_method_frame_info.h"
#include "stack_map.h"
diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h
index 106c7f1..493ea85 100644
--- a/runtime/subtype_check.h
+++ b/runtime/subtype_check.h
@@ -20,7 +20,7 @@
#include "subtype_check_bits_and_status.h"
#include "subtype_check_info.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "mirror/class.h"
#include "runtime.h"
diff --git a/runtime/thread.h b/runtime/thread.h
index ccde236..6db1943 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -30,8 +30,8 @@
#include "base/atomic.h"
#include "base/enums.h"
#include "base/globals.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "base/safe_map.h"
#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -563,11 +563,11 @@
bool Interrupted();
// Implements java.lang.Thread.isInterrupted.
bool IsInterrupted();
- void Interrupt(Thread* self) REQUIRES(!*wait_mutex_);
+ void Interrupt(Thread* self) REQUIRES(!wait_mutex_);
void SetInterrupted(bool i) {
tls32_.interrupted.store(i, std::memory_order_seq_cst);
}
- void Notify() REQUIRES(!*wait_mutex_);
+ void Notify() REQUIRES(!wait_mutex_);
ALWAYS_INLINE void PoisonObjectPointers() {
++poison_object_cookie_;
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 0f96510..de698c2 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -136,26 +136,33 @@
waiting_count_(0),
start_time_(0),
total_wait_time_(0),
- // Add one since the caller of constructor waits on the barrier too.
- creation_barier_(num_threads + 1),
+ creation_barier_(0),
max_active_workers_(num_threads),
- create_peers_(create_peers) {
- while (GetThreadCount() < num_threads) {
- const std::string worker_name = StringPrintf("%s worker thread %zu", name_.c_str(),
- GetThreadCount());
- threads_.push_back(new ThreadPoolWorker(this, worker_name, worker_stack_size));
+ create_peers_(create_peers),
+ worker_stack_size_(worker_stack_size) {
+ CreateThreads();
+}
+
+void ThreadPool::CreateThreads() {
+ CHECK(threads_.empty());
+ Thread* self = Thread::Current();
+ {
+ MutexLock mu(self, task_queue_lock_);
+ shutting_down_ = false;
+ // Add one since the caller of constructor waits on the barrier too.
+ creation_barier_.Init(self, max_active_workers_ + 1);
+ while (GetThreadCount() < max_active_workers_) {
+ const std::string worker_name = StringPrintf("%s worker thread %zu", name_.c_str(),
+ GetThreadCount());
+ threads_.push_back(
+ new ThreadPoolWorker(this, worker_name, worker_stack_size_));
+ }
}
// Wait for all of the threads to attach.
creation_barier_.Wait(Thread::Current());
}
-void ThreadPool::SetMaxActiveWorkers(size_t threads) {
- MutexLock mu(Thread::Current(), task_queue_lock_);
- CHECK_LE(threads, GetThreadCount());
- max_active_workers_ = threads;
-}
-
-ThreadPool::~ThreadPool() {
+void ThreadPool::DeleteThreads() {
{
Thread* self = Thread::Current();
MutexLock mu(self, task_queue_lock_);
@@ -165,10 +172,22 @@
task_queue_condition_.Broadcast(self);
completion_condition_.Broadcast(self);
}
- // Wait for the threads to finish.
+ // Wait for the threads to finish. We expect the user of the pool
+ // not to run multi-threaded calls to `CreateThreads` and `DeleteThreads`,
+ // so we don't guard the field here.
STLDeleteElements(&threads_);
}
+void ThreadPool::SetMaxActiveWorkers(size_t max_workers) {
+ MutexLock mu(Thread::Current(), task_queue_lock_);
+ CHECK_LE(max_workers, GetThreadCount());
+ max_active_workers_ = max_workers;
+}
+
+ThreadPool::~ThreadPool() {
+ DeleteThreads();
+}
+
void ThreadPool::StartWorkers(Thread* self) {
MutexLock mu(self, task_queue_lock_);
started_ = true;
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index fee009b..f55d72e 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -129,6 +129,12 @@
size_t worker_stack_size = ThreadPoolWorker::kDefaultStackSize);
virtual ~ThreadPool();
+ // Create the threads of this pool.
+ void CreateThreads();
+
+ // Stops and deletes all threads in this pool.
+ void DeleteThreads();
+
// Wait for all tasks currently on queue to get completed. If the pool has been stopped, only
// wait till all already running tasks are done.
// When the pool was created with peers for workers, do_work must not be true (see ThreadPool()).
@@ -174,7 +180,6 @@
// How many worker threads are waiting on the condition.
volatile size_t waiting_count_ GUARDED_BY(task_queue_lock_);
std::deque<Task*> tasks_ GUARDED_BY(task_queue_lock_);
- // TODO: make this immutable/const?
std::vector<ThreadPoolWorker*> threads_;
// Work balance detection.
uint64_t start_time_ GUARDED_BY(task_queue_lock_);
@@ -182,6 +187,7 @@
Barrier creation_barier_;
size_t max_active_workers_ GUARDED_BY(task_queue_lock_);
const bool create_peers_;
+ const size_t worker_stack_size_;
private:
friend class ThreadPoolWorker;
diff --git a/runtime/trace.h b/runtime/trace.h
index 926a34f..1089962 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -28,6 +28,7 @@
#include "base/atomic.h"
#include "base/globals.h"
+#include "base/locks.h"
#include "base/macros.h"
#include "base/os.h"
#include "base/safe_map.h"
@@ -42,6 +43,7 @@
class ArtField;
class ArtMethod;
class DexFile;
+class LOCKABLE Mutex;
class ShadowFrame;
class Thread;
@@ -173,57 +175,57 @@
uint32_t GetClockOverheadNanoSeconds();
void CompareAndUpdateStackTrace(Thread* thread, std::vector<ArtMethod*>* stack_trace)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_);
// InstrumentationListener implementation.
void MethodEntered(Thread* thread,
Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_)
override;
void MethodExited(Thread* thread,
Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc,
const JValue& return_value)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_)
override;
void MethodUnwind(Thread* thread,
Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_)
override;
void DexPcMoved(Thread* thread,
Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t new_dex_pc)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_)
override;
void FieldRead(Thread* thread,
Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc,
ArtField* field)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) override;
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_) override;
void FieldWritten(Thread* thread,
Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc,
ArtField* field,
const JValue& field_value)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) override;
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_) override;
void ExceptionThrown(Thread* thread,
Handle<mirror::Throwable> exception_object)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) override;
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_) override;
void ExceptionHandled(Thread* thread, Handle<mirror::Throwable> exception_object)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) override;
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_) override;
void Branch(Thread* thread,
ArtMethod* method,
uint32_t dex_pc,
int32_t dex_pc_offset)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) override;
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_) override;
void WatchedFramePop(Thread* thread, const ShadowFrame& frame)
REQUIRES_SHARED(Locks::mutator_lock_) override;
// Reuse an old stack trace if it exists, otherwise allocate a new one.
@@ -258,20 +260,20 @@
// how to annotate this.
NO_THREAD_SAFETY_ANALYSIS;
void FinishTracing()
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_);
void ReadClocks(Thread* thread, uint32_t* thread_clock_diff, uint32_t* wall_clock_diff);
void LogMethodTraceEvent(Thread* thread, ArtMethod* method,
instrumentation::Instrumentation::InstrumentationEvent event,
uint32_t thread_clock_diff, uint32_t wall_clock_diff)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_);
// Methods to output traced methods and threads.
void GetVisitedMethods(size_t end_offset, std::set<ArtMethod*>* visited_methods)
- REQUIRES(!*unique_methods_lock_);
+ REQUIRES(!unique_methods_lock_);
void DumpMethodList(std::ostream& os, const std::set<ArtMethod*>& visited_methods)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_);
void DumpThreadList(std::ostream& os) REQUIRES(!Locks::thread_list_lock_);
// Methods to register seen entitites in streaming mode. The methods return true if the entity
@@ -289,15 +291,15 @@
void FlushBuf()
REQUIRES(streaming_lock_);
- uint32_t EncodeTraceMethod(ArtMethod* method) REQUIRES(!*unique_methods_lock_);
+ uint32_t EncodeTraceMethod(ArtMethod* method) REQUIRES(!unique_methods_lock_);
uint32_t EncodeTraceMethodAndAction(ArtMethod* method, TraceAction action)
- REQUIRES(!*unique_methods_lock_);
- ArtMethod* DecodeTraceMethod(uint32_t tmid) REQUIRES(!*unique_methods_lock_);
- std::string GetMethodLine(ArtMethod* method) REQUIRES(!*unique_methods_lock_)
+ REQUIRES(!unique_methods_lock_);
+ ArtMethod* DecodeTraceMethod(uint32_t tmid) REQUIRES(!unique_methods_lock_);
+ std::string GetMethodLine(ArtMethod* method) REQUIRES(!unique_methods_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
void DumpBuf(uint8_t* buf, size_t buf_size, TraceClockSource clock_source)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_);
// Singleton instance of the Trace or null when no method tracing is active.
static Trace* volatile the_trace_ GUARDED_BY(Locks::trace_lock_);
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 3099b23..3369784 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -24,8 +24,8 @@
#include "base/arena_object.h"
#include "base/bit_vector.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "base/stringpiece.h"
#include "dex/primitive.h"
#include "gc_root.h"
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 9bb60bb..de66bf5 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -23,7 +23,7 @@
#include <android-base/logging.h>
-#include "base/mutex.h"
+#include "base/locks.h"
#include "base/safe_map.h"
#include "base/scoped_arena_containers.h"
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index b666c15..d346a95 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -22,6 +22,7 @@
#include "art_method-inl.h"
#include "base/indenter.h"
#include "base/leb128.h"
+#include "base/mutex-inl.h"
#include "base/stl_util.h"
#include "compiler_callbacks.h"
#include "dex/dex_file-inl.h"
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 0146b17..dfd4a5c 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -22,7 +22,7 @@
#include <vector>
#include "base/array_ref.h"
-#include "base/mutex.h"
+#include "base/locks.h"
#include "dex/dex_file_types.h"
#include "handle.h"
#include "obj_ptr.h"
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 0b7ed09..66cbbec 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_WELL_KNOWN_CLASSES_H_
#define ART_RUNTIME_WELL_KNOWN_CLASSES_H_
-#include "base/mutex.h"
+#include "base/locks.h"
#include "jni.h"
#include "obj_ptr.h"
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeHandlerTest.java
index 9d2f014..9f924b2 100644
--- a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeHandlerTest.java
+++ b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeHandlerTest.java
@@ -67,7 +67,7 @@
" @Annotation(returnType=Integer.class)",
" public String method() {return null;}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION,
@@ -91,7 +91,7 @@
" @Annotation(returnType=Integer.class)",
" public String method() {return null;}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION,
@@ -113,7 +113,7 @@
" @Annotation(returnType=Integer.class)",
" public String method() {return null;}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION,
@@ -138,7 +138,7 @@
" @Annotation(returnType=Integer.class)",
" public String field;",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION,
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java
index 1202564..25f2844 100644
--- a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java
+++ b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java
@@ -74,7 +74,7 @@
" @Annotation(returnType=Long.class)",
" public String method() {return null;}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of("Lannotation/Annotation$Multi;",
@@ -104,7 +104,7 @@
" @Annotation(returnType=Long.class)",
" public String method() {return null;}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of("Lannotation/Annotation$Multi;",
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java
index cdf01af..dc767fe 100644
--- a/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java
+++ b/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java
@@ -82,7 +82,7 @@
" @Anno",
" public void method() {}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
@@ -103,7 +103,7 @@
" @Anno",
" public Class() {}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
@@ -124,7 +124,7 @@
" @Anno",
" public int i;",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
@@ -145,7 +145,7 @@
" @Anno(expectedSignature=\"La/b/Class;->method()V\")",
" public void method() {}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
@@ -166,7 +166,7 @@
" @Anno(expectedSignature=\"La/b/Class;->nomethod()V\")",
" public void method() {}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
@@ -186,7 +186,7 @@
" public void method() {}",
" }",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class$Inner"), mStatus,
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
@@ -205,7 +205,7 @@
"public class Class {",
" public void method() {}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
@@ -224,7 +224,7 @@
" @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")",
" public void method(T arg) {}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
@@ -252,7 +252,7 @@
" @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")",
" public void method(T arg) {}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP));
@@ -284,7 +284,7 @@
" @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")",
" public void method(T arg) {}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP));
@@ -320,7 +320,7 @@
"package a.b;",
"public class Class extends Base implements Interface {",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP));
@@ -354,7 +354,7 @@
" @Anno",
" public void method(T arg) {}",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Set<String> publicApis = Sets.newHashSet(
"La/b/Base;->method(Ljava/lang/Object;)V",
@@ -385,7 +385,7 @@
" @Anno(expectedSignature=\"La/b/Class;->field:I\")",
" public volatile int field;",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION, createGreylistHandler(
@@ -407,7 +407,7 @@
" @Anno(expectedSignature=\"La/b/Class;->wrong:I\")",
" public volatile int field;",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP));
@@ -424,7 +424,7 @@
" @Anno(maxTargetSdk=1)",
" public int field;",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION, createGreylistHandler(
@@ -444,7 +444,7 @@
" @Anno",
" public int field;",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION, createGreylistHandler(
@@ -464,7 +464,7 @@
" @Anno(maxTargetSdk=2)",
" public int field;",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
Map<String, AnnotationHandler> handlerMap =
ImmutableMap.of(ANNOTATION, createGreylistHandler(
@@ -493,7 +493,7 @@
" @Anno2(maxTargetSdk=2, trackingBug=123456789)",
" public int field;",
"}"));
- assertThat(mJavac.compile()).isTrue();
+ mJavac.compile();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
ImmutableMap.of("Lannotation/Anno2;", createGreylistHandler(x -> true,
ImmutableMap.of(2, "flag2")))
diff --git a/tools/class2greylist/test/src/com/android/javac/Javac.java b/tools/class2greylist/test/src/com/android/javac/Javac.java
index 202f412..94e4e49 100644
--- a/tools/class2greylist/test/src/com/android/javac/Javac.java
+++ b/tools/class2greylist/test/src/com/android/javac/Javac.java
@@ -18,6 +18,7 @@
import com.google.common.io.Files;
+import java.util.stream.Collectors;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
@@ -76,15 +77,24 @@
return this;
}
- public boolean compile() {
+ public void compile() {
+ DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
JavaCompiler.CompilationTask task = mJavac.getTask(
null,
mFileMan,
- null,
+ diagnosticCollector,
null,
null,
mCompilationUnits);
- return task.call();
+ boolean result = task.call();
+ if (!result) {
+ throw new IllegalStateException(
+ "Compilation failed:" +
+ diagnosticCollector.getDiagnostics()
+ .stream()
+ .map(Object::toString)
+ .collect(Collectors.joining("\n")));
+ }
}
public InputStream getClassFile(String classname) throws IOException {