| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "vm/heap/safepoint.h" |
| |
| #include "vm/thread.h" |
| #include "vm/thread_registry.h" |
| |
| namespace dart { |
| |
| DEFINE_FLAG(bool, trace_safepoint, false, "Trace Safepoint logic."); |
| |
| SafepointOperationScope::SafepointOperationScope(Thread* T) |
| : ThreadStackResource(T) { |
| ASSERT(T != NULL); |
| Isolate* I = T->isolate(); |
| ASSERT(I != NULL); |
| |
| SafepointHandler* handler = I->safepoint_handler(); |
| ASSERT(handler != NULL); |
| |
| // Signal all threads to get to a safepoint and wait for them to |
| // get to a safepoint. |
| handler->SafepointThreads(T); |
| } |
| |
| SafepointOperationScope::~SafepointOperationScope() { |
| Thread* T = thread(); |
| ASSERT(T != NULL); |
| Isolate* I = T->isolate(); |
| ASSERT(I != NULL); |
| |
| // Resume all threads which are blocked for the safepoint operation. |
| SafepointHandler* handler = I->safepoint_handler(); |
| ASSERT(handler != NULL); |
| handler->ResumeThreads(T); |
| } |
| |
| SafepointHandler::SafepointHandler(Isolate* isolate) |
| : isolate_(isolate), |
| safepoint_lock_(), |
| number_threads_not_at_safepoint_(0), |
| safepoint_operation_count_(0), |
| owner_(NULL) {} |
| |
| SafepointHandler::~SafepointHandler() { |
| ASSERT(owner_ == NULL); |
| ASSERT(safepoint_operation_count_ == 0); |
| isolate_ = NULL; |
| } |
| |
| void SafepointHandler::SafepointThreads(Thread* T) { |
| ASSERT(T->no_safepoint_scope_depth() == 0); |
| ASSERT(T->execution_state() == Thread::kThreadInVM); |
| |
| { |
| // First grab the threads list lock for this isolate |
| // and check if a safepoint is already in progress. This |
| // ensures that two threads do not start a safepoint operation |
| // at the same time. |
| MonitorLocker sl(threads_lock()); |
| |
| // Now check to see if a safepoint operation is already in progress |
| // for this isolate, block if an operation is in progress. |
| while (SafepointInProgress()) { |
| // If we are recursively invoking a Safepoint operation then we |
| // just increment the count and return, otherwise we wait for the |
| // safepoint operation to be done. |
| if (owner_ == T) { |
| increment_safepoint_operation_count(); |
| return; |
| } |
| sl.WaitWithSafepointCheck(T); |
| } |
| |
| // Set safepoint in progress state by this thread. |
| SetSafepointInProgress(T); |
| |
| // Go over the active thread list and ensure that all threads active |
| // in the isolate reach a safepoint. |
| Thread* current = isolate()->thread_registry()->active_list(); |
| while (current != NULL) { |
| MonitorLocker tl(current->thread_lock()); |
| if (!current->BypassSafepoints()) { |
| if (current == T) { |
| current->SetAtSafepoint(true); |
| } else { |
| uint32_t state = current->SetSafepointRequested(true); |
| if (!Thread::IsAtSafepoint(state)) { |
| // Thread is not already at a safepoint so try to |
| // get it to a safepoint and wait for it to check in. |
| if (current->IsMutatorThread()) { |
| ASSERT(T->isolate() != NULL); |
| current->ScheduleInterruptsLocked(Thread::kVMInterrupt); |
| } |
| MonitorLocker sl(&safepoint_lock_); |
| ++number_threads_not_at_safepoint_; |
| } |
| } |
| } |
| current = current->next(); |
| } |
| } |
| // Now wait for all threads that are not already at a safepoint to check-in. |
| { |
| MonitorLocker sl(&safepoint_lock_); |
| intptr_t num_attempts = 0; |
| while (number_threads_not_at_safepoint_ > 0) { |
| Monitor::WaitResult retval = sl.Wait(1000); |
| if (retval == Monitor::kTimedOut) { |
| num_attempts += 1; |
| if (FLAG_trace_safepoint && num_attempts > 10) { |
| // We have been waiting too long, start logging this as we might |
| // have an issue where a thread is not checking in for a safepoint. |
| for (Thread* current = isolate()->thread_registry()->active_list(); |
| current != NULL; current = current->next()) { |
| if (!current->IsAtSafepoint()) { |
| OS::PrintErr("Attempt:%" Pd |
| " waiting for thread %s to check in\n", |
| num_attempts, current->os_thread()->name()); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void SafepointHandler::ResumeThreads(Thread* T) { |
| // First resume all the threads which are blocked for the safepoint |
| // operation. |
| MonitorLocker sl(threads_lock()); |
| |
| // First check if we are in a recursive safepoint operation, in that case |
| // we just decrement safepoint_operation_count and return. |
| ASSERT(SafepointInProgress()); |
| if (safepoint_operation_count() > 1) { |
| decrement_safepoint_operation_count(); |
| return; |
| } |
| Thread* current = isolate()->thread_registry()->active_list(); |
| while (current != NULL) { |
| MonitorLocker tl(current->thread_lock()); |
| if (!current->BypassSafepoints()) { |
| if (current == T) { |
| current->SetAtSafepoint(false); |
| } else { |
| uint32_t state = current->SetSafepointRequested(false); |
| if (Thread::IsBlockedForSafepoint(state)) { |
| tl.Notify(); |
| } |
| } |
| } |
| current = current->next(); |
| } |
| // Now reset the safepoint_in_progress_ state and notify all threads |
| // that are waiting to enter the isolate or waiting to start another |
| // safepoint operation. |
| ResetSafepointInProgress(T); |
| sl.NotifyAll(); |
| } |
| |
| void SafepointHandler::EnterSafepointUsingLock(Thread* T) { |
| MonitorLocker tl(T->thread_lock()); |
| T->SetAtSafepoint(true); |
| if (T->IsSafepointRequested()) { |
| MonitorLocker sl(&safepoint_lock_); |
| ASSERT(number_threads_not_at_safepoint_ > 0); |
| number_threads_not_at_safepoint_ -= 1; |
| sl.Notify(); |
| } |
| } |
| |
| void SafepointHandler::ExitSafepointUsingLock(Thread* T) { |
| MonitorLocker tl(T->thread_lock()); |
| ASSERT(T->IsAtSafepoint()); |
| while (T->IsSafepointRequested()) { |
| T->SetBlockedForSafepoint(true); |
| tl.Wait(); |
| T->SetBlockedForSafepoint(false); |
| } |
| T->SetAtSafepoint(false); |
| } |
| |
| void SafepointHandler::BlockForSafepoint(Thread* T) { |
| ASSERT(!T->BypassSafepoints()); |
| MonitorLocker tl(T->thread_lock()); |
| if (T->IsSafepointRequested()) { |
| T->SetAtSafepoint(true); |
| { |
| MonitorLocker sl(&safepoint_lock_); |
| ASSERT(number_threads_not_at_safepoint_ > 0); |
| number_threads_not_at_safepoint_ -= 1; |
| sl.Notify(); |
| } |
| while (T->IsSafepointRequested()) { |
| T->SetBlockedForSafepoint(true); |
| tl.Wait(); |
| T->SetBlockedForSafepoint(false); |
| } |
| T->SetAtSafepoint(false); |
| } |
| } |
| |
| } // namespace dart |