|  | // 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/safepoint.h" | 
|  |  | 
|  | #include "vm/thread.h" | 
|  | #include "vm/thread_registry.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | SafepointOperationScope::SafepointOperationScope(Thread* T) : StackResource(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_(new Monitor()), | 
|  | number_threads_not_at_safepoint_(0), | 
|  | safepoint_operation_count_(0), | 
|  | owner_(NULL) {} | 
|  |  | 
|  | SafepointHandler::~SafepointHandler() { | 
|  | ASSERT(owner_ == NULL); | 
|  | ASSERT(safepoint_operation_count_ == 0); | 
|  | delete safepoint_lock_; | 
|  | safepoint_lock_ = NULL; | 
|  | 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 != T) { | 
|  | 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_; | 
|  | } | 
|  | } else { | 
|  | current->SetAtSafepoint(true); | 
|  | } | 
|  | 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 (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. | 
|  | OS::PrintErr("Attempt:%" Pd " waiting for %d threads to check in\n", | 
|  | num_attempts, number_threads_not_at_safepoint_); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 != T) { | 
|  | uint32_t state = current->SetSafepointRequested(false); | 
|  | if (Thread::IsBlockedForSafepoint(state)) { | 
|  | tl.Notify(); | 
|  | } | 
|  | } else { | 
|  | current->SetAtSafepoint(false); | 
|  | } | 
|  | 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) { | 
|  | 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 |