More of the concurrent copying collector.
Bug: 12687968
Change-Id: I62f70274d47df6d6cab714df95c518b750ce3105
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 5690d51..17dfd8c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -33,6 +33,7 @@
#include "arch/context.h"
#include "base/mutex.h"
+#include "base/timing_logger.h"
#include "base/to_str.h"
#include "class_linker-inl.h"
#include "class_linker.h"
@@ -723,13 +724,34 @@
return success;
}
+Closure* Thread::GetFlipFunction() {
+ Atomic<Closure*>* atomic_func = reinterpret_cast<Atomic<Closure*>*>(&tlsPtr_.flip_function);
+ Closure* func;
+ do {
+ func = atomic_func->LoadRelaxed();
+ if (func == nullptr) {
+ return nullptr;
+ }
+ } while (!atomic_func->CompareExchangeWeakSequentiallyConsistent(func, nullptr));
+ DCHECK(func != nullptr);
+ return func;
+}
+
+void Thread::SetFlipFunction(Closure* function) {
+ CHECK(function != nullptr);
+ Atomic<Closure*>* atomic_func = reinterpret_cast<Atomic<Closure*>*>(&tlsPtr_.flip_function);
+ atomic_func->StoreSequentiallyConsistent(function);
+}
+
void Thread::FullSuspendCheck() {
VLOG(threads) << this << " self-suspending";
ATRACE_BEGIN("Full suspend check");
// Make thread appear suspended to other threads, release mutator_lock_.
+ tls32_.suspended_at_suspend_check = true;
TransitionFromRunnableToSuspended(kSuspended);
// Transition back to runnable noting requests to suspend, re-acquire share on mutator_lock_.
TransitionFromSuspendedToRunnable();
+ tls32_.suspended_at_suspend_check = false;
ATRACE_END();
VLOG(threads) << this << " self-reviving";
}
@@ -740,6 +762,20 @@
bool is_daemon = false;
Thread* self = Thread::Current();
+ // If flip_function is not null, it means we have run a checkpoint
+ // before the thread wakes up to execute the flip function and the
+ // thread roots haven't been forwarded. So the following access to
+ // the roots (opeer or methods in the frames) would be bad. Run it
+ // here. TODO: clean up.
+ if (thread != nullptr) {
+ ScopedObjectAccessUnchecked soa(self);
+ Thread* this_thread = const_cast<Thread*>(thread);
+ Closure* flip_func = this_thread->GetFlipFunction();
+ if (flip_func != nullptr) {
+ flip_func->Run(this_thread);
+ }
+ }
+
// Don't do this if we are aborting since the GC may have all the threads suspended. This will
// cause ScopedObjectAccessUnchecked to deadlock.
if (gAborting == 0 && self != nullptr && thread != nullptr && thread->tlsPtr_.opeer != nullptr) {
@@ -980,6 +1016,19 @@
}
void Thread::DumpJavaStack(std::ostream& os) const {
+ // If flip_function is not null, it means we have run a checkpoint
+ // before the thread wakes up to execute the flip function and the
+ // thread roots haven't been forwarded. So the following access to
+ // the roots (locks or methods in the frames) would be bad. Run it
+ // here. TODO: clean up.
+ {
+ Thread* this_thread = const_cast<Thread*>(this);
+ Closure* flip_func = this_thread->GetFlipFunction();
+ if (flip_func != nullptr) {
+ flip_func->Run(this_thread);
+ }
+ }
+
// Dumping the Java stack involves the verifier for locks. The verifier operates under the
// assumption that there is no exception pending on entry. Thus, stash any pending exception.
// Thread::Current() instead of this in case a thread is dumping the stack of another suspended
@@ -1115,6 +1164,8 @@
for (uint32_t i = 0; i < kMaxCheckpoints; ++i) {
tlsPtr_.checkpoint_functions[i] = nullptr;
}
+ tlsPtr_.flip_function = nullptr;
+ tls32_.suspended_at_suspend_check = false;
}
bool Thread::IsStillStarting() const {
@@ -1233,6 +1284,8 @@
CHECK(tlsPtr_.checkpoint_functions[0] == nullptr);
CHECK(tlsPtr_.checkpoint_functions[1] == nullptr);
CHECK(tlsPtr_.checkpoint_functions[2] == nullptr);
+ CHECK(tlsPtr_.flip_function == nullptr);
+ CHECK_EQ(tls32_.suspended_at_suspend_check, false);
// We may be deleting a still born thread.
SetStateUnsafe(kTerminated);