| // 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/heap/heap.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, | 
 |                                                  SafepointLevel level) | 
 |     : ThreadStackResource(T), level_(level) { | 
 |   ASSERT(T != nullptr && T->isolate_group() != nullptr); | 
 |  | 
 |   auto handler = T->isolate_group()->safepoint_handler(); | 
 |   handler->SafepointThreads(T, level_); | 
 | } | 
 |  | 
 | SafepointOperationScope::~SafepointOperationScope() { | 
 |   Thread* T = thread(); | 
 |   ASSERT(T != nullptr && T->isolate_group() != nullptr); | 
 |  | 
 |   auto handler = T->isolate_group()->safepoint_handler(); | 
 |   handler->ResumeThreads(T, level_); | 
 | } | 
 |  | 
 | ForceGrowthSafepointOperationScope::ForceGrowthSafepointOperationScope( | 
 |     Thread* T, | 
 |     SafepointLevel level) | 
 |     : ThreadStackResource(T), level_(level) { | 
 |   ASSERT(T != nullptr); | 
 |   IsolateGroup* IG = T->isolate_group(); | 
 |   ASSERT(IG != nullptr); | 
 |  | 
 |   T->IncrementForceGrowthScopeDepth(); | 
 |  | 
 |   auto handler = IG->safepoint_handler(); | 
 |   handler->SafepointThreads(T, level_); | 
 | } | 
 |  | 
 | ForceGrowthSafepointOperationScope::~ForceGrowthSafepointOperationScope() { | 
 |   Thread* T = thread(); | 
 |   ASSERT(T != nullptr); | 
 |   IsolateGroup* IG = T->isolate_group(); | 
 |   ASSERT(IG != nullptr); | 
 |  | 
 |   auto handler = IG->safepoint_handler(); | 
 |   handler->ResumeThreads(T, level_); | 
 |  | 
 |   T->DecrementForceGrowthScopeDepth(); | 
 |   if (!T->force_growth()) { | 
 |     // Check if we passed the growth limit during the scope. | 
 |     T->heap()->CheckCatchUp(T); | 
 |   } | 
 | } | 
 |  | 
 | SafepointHandler::SafepointHandler(IsolateGroup* isolate_group) | 
 |     : isolate_group_(isolate_group) { | 
 |   handlers_[SafepointLevel::kGC] = | 
 |       new LevelHandler(isolate_group, SafepointLevel::kGC); | 
 |   handlers_[SafepointLevel::kGCAndDeopt] = | 
 |       new LevelHandler(isolate_group, SafepointLevel::kGCAndDeopt); | 
 |   handlers_[SafepointLevel::kGCAndDeoptAndReload] = | 
 |       new LevelHandler(isolate_group, SafepointLevel::kGCAndDeoptAndReload); | 
 | } | 
 |  | 
 | SafepointHandler::~SafepointHandler() { | 
 |   for (intptr_t level = 0; level < SafepointLevel::kNumLevels; ++level) { | 
 |     ASSERT(handlers_[level]->owner_ == nullptr); | 
 |     delete handlers_[level]; | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::SafepointThreads(Thread* T, SafepointLevel level) { | 
 |   ASSERT(T->no_safepoint_scope_depth() == 0); | 
 |   ASSERT(T->execution_state() == Thread::kThreadInVM); | 
 |   ASSERT(T->current_safepoint_level() >= level); | 
 |  | 
 |   MallocGrowableArray<Dart_Port> oob_isolates; | 
 |   { | 
 |     MonitorLocker tl(threads_lock()); | 
 |  | 
 |     // Allow recursive deopt safepoint operation. | 
 |     if (handlers_[level]->owner_ == T) { | 
 |       // If we own this safepoint level already we have to own the lower levels | 
 |       // as well. | 
 |       AssertWeOwnLowerLevelSafepoints(T, level); | 
 |  | 
 |       for (intptr_t i = 0; i <= level; ++i) { | 
 |         handlers_[i]->operation_count_++; | 
 |       } | 
 |       return; | 
 |     } | 
 |  | 
 |     // This level of nesting is not allowed (this thread cannot own lower levels | 
 |     // and then later try acquire higher levels). | 
 |     AssertWeDoNotOwnLowerLevelSafepoints(T, level); | 
 |  | 
 |     // Mark this thread at safepoint and possibly notify waiting threads. | 
 |     { | 
 |       MonitorLocker tl(T->thread_lock()); | 
 |       // We only enter [level] here. That means a higher level that is waiting | 
 |       // for us to check-in will not consider us as not parked. This is required | 
 |       // since we are not actually parked (we can finish running this method and | 
 |       // then caller continues). | 
 |       EnterSafepointLocked(T, &tl, level); | 
 |     } | 
 |  | 
 |     // Wait until other safepoint operations are done & mark us as owning | 
 |     // the safepoint - so no other thread can. | 
 |     while (handlers_[level]->SafepointInProgress()) { | 
 |       tl.Wait(); | 
 |     } | 
 |     handlers_[level]->SetSafepointInProgress(T); | 
 |  | 
 |     // Ensure a thread is at a safepoint or notify it to get to one. | 
 |     handlers_[level]->NotifyThreadsToGetToSafepointLevel(T, &oob_isolates); | 
 |   } | 
 |  | 
 |   for (auto main_port : oob_isolates) { | 
 |     Isolate::SendInternalLibMessage(main_port, Isolate::kCheckForReload, | 
 |                                     /*ignored=*/-1); | 
 |   } | 
 |  | 
 |   // Now wait for all threads that are not already at a safepoint to check-in. | 
 |   handlers_[level]->WaitUntilThreadsReachedSafepointLevel(); | 
 |  | 
 |   // No other mutator is running at this point. We'll set ourselves as owners of | 
 |   // all the lower levels as well - since higher levels provide even more | 
 |   // guarantees that lower levels (e.g. others being stopped at places where | 
 |   // one can deopt also implies one can gc) | 
 |   AcquireLowerLevelSafepoints(T, level); | 
 |  | 
 |   // The current thread owns the safepoint, but it will continue to run and as | 
 |   // such is not at any "point" that can be considered safe. | 
 |   { | 
 |     MonitorLocker tl(T->thread_lock()); | 
 |     ExitSafepointLocked(T, &tl, level); | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::AssertWeOwnLowerLevelSafepoints(Thread* T, | 
 |                                                        SafepointLevel level) { | 
 |   for (intptr_t lower_level = level - 1; lower_level >= 0; --lower_level) { | 
 |     RELEASE_ASSERT(handlers_[lower_level]->owner_ == T); | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::AssertWeDoNotOwnLowerLevelSafepoints( | 
 |     Thread* T, | 
 |     SafepointLevel level) { | 
 |   for (intptr_t lower_level = level - 1; lower_level >= 0; --lower_level) { | 
 |     RELEASE_ASSERT(handlers_[lower_level]->owner_ != T); | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::LevelHandler::NotifyThreadsToGetToSafepointLevel( | 
 |     Thread* T, | 
 |     MallocGrowableArray<Dart_Port>* oob_isolates) { | 
 |   ASSERT(num_threads_not_parked_ == 0); | 
 |   for (auto current = isolate_group()->thread_registry()->active_list(); | 
 |        current != nullptr; current = current->next()) { | 
 |     MonitorLocker tl(current->thread_lock()); | 
 |     if (!current->BypassSafepoints() && current != T) { | 
 |       const uint32_t state = current->SetSafepointRequested(level_, true); | 
 |       if (!Thread::IsAtSafepoint(level_, state)) { | 
 |         if (level_ == SafepointLevel::kGCAndDeoptAndReload) { | 
 |           // Interrupt the mutator by sending an reload OOB message. The | 
 |           // mutator will only check-in once it's handling the reload OOB | 
 |           // message. | 
 |           // | 
 |           // If there's no isolate, it may be a helper thread that has entered | 
 |           // via `Thread::EnterIsolateGroupAsHelper()`. In that case we cannot | 
 |           // send an OOB message. Instead we'll have to wait until that thread | 
 |           // de-schedules itself. | 
 |           auto isolate = current->scheduled_dart_mutator_isolate(); | 
 |           if (isolate != nullptr) { | 
 |             oob_isolates->Add(isolate->main_port()); | 
 |           } | 
 |         } else { | 
 |           // Interrupt the mutator and ask it to block at any interruption | 
 |           // point. | 
 |           if (current->IsDartMutatorThread()) { | 
 |             current->ScheduleInterrupts(Thread::kVMInterrupt); | 
 |           } | 
 |         } | 
 |         MonitorLocker sl(&parked_lock_); | 
 |         num_threads_not_parked_++; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::ResumeThreads(Thread* T, SafepointLevel level) { | 
 |   { | 
 |     MonitorLocker sl(threads_lock()); | 
 |  | 
 |     ASSERT(handlers_[level]->SafepointInProgress()); | 
 |     ASSERT(handlers_[level]->owner_ == T); | 
 |     AssertWeOwnLowerLevelSafepoints(T, level); | 
 |  | 
 |     // We allow recursive safepoints. | 
 |     if (handlers_[level]->operation_count_ > 1) { | 
 |       for (intptr_t i = 0; i <= level; ++i) { | 
 |         handlers_[i]->operation_count_--; | 
 |       } | 
 |       return; | 
 |     } | 
 |  | 
 |     ReleaseLowerLevelSafepoints(T, level); | 
 |     handlers_[level]->ResetSafepointInProgress(T); | 
 |     handlers_[level]->NotifyThreadsToContinue(T); | 
 |     sl.NotifyAll(); | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::LevelHandler::WaitUntilThreadsReachedSafepointLevel() { | 
 |   MonitorLocker sl(&parked_lock_); | 
 |   intptr_t num_attempts = 0; | 
 |   while (num_threads_not_parked_ > 0) { | 
 |     Monitor::WaitResult retval = sl.Wait(1000); | 
 |     if (retval == Monitor::kTimedOut) { | 
 |       num_attempts += 1; | 
 |       if (FLAG_trace_safepoint && num_attempts > 10) { | 
 |         for (auto current = isolate_group()->thread_registry()->active_list(); | 
 |              current != nullptr; current = current->next()) { | 
 |           if (!current->IsAtSafepoint(level_)) { | 
 |             OS::PrintErr("Attempt:%" Pd " waiting for thread %s to check in\n", | 
 |                          num_attempts, current->os_thread()->name()); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::AcquireLowerLevelSafepoints(Thread* T, | 
 |                                                    SafepointLevel level) { | 
 |   MonitorLocker tl(threads_lock()); | 
 |   ASSERT(handlers_[level]->owner_ == T); | 
 |   for (intptr_t lower_level = level - 1; lower_level >= 0; --lower_level) { | 
 |     ASSERT(!handlers_[lower_level]->SafepointInProgress()); | 
 |     handlers_[lower_level]->SetSafepointInProgress(T); | 
 |     ASSERT(handlers_[lower_level]->owner_ == T); | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::ReleaseLowerLevelSafepoints(Thread* T, | 
 |                                                    SafepointLevel level) { | 
 |   for (intptr_t lower_level = 0; lower_level < level; ++lower_level) { | 
 |     handlers_[lower_level]->ResetSafepointInProgress(T); | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::LevelHandler::NotifyThreadsToContinue(Thread* T) { | 
 |   for (auto current = isolate_group()->thread_registry()->active_list(); | 
 |        current != nullptr; current = current->next()) { | 
 |     MonitorLocker tl(current->thread_lock()); | 
 |     if (!current->BypassSafepoints() && current != T) { | 
 |       bool resume = false; | 
 |       for (intptr_t lower_level = level_; lower_level >= 0; --lower_level) { | 
 |         if (Thread::IsBlockedForSafepoint(current->SetSafepointRequested( | 
 |                 static_cast<SafepointLevel>(lower_level), false))) { | 
 |           resume = true; | 
 |         } | 
 |       } | 
 |       if (resume) { | 
 |         tl.Notify(); | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::EnterSafepointUsingLock(Thread* T) { | 
 |   MonitorLocker tl(T->thread_lock()); | 
 |   EnterSafepointLocked(T, &tl, T->current_safepoint_level()); | 
 | } | 
 |  | 
 | void SafepointHandler::ExitSafepointUsingLock(Thread* T) { | 
 |   MonitorLocker tl(T->thread_lock()); | 
 |   ASSERT(T->IsAtSafepoint()); | 
 |   ExitSafepointLocked(T, &tl, T->current_safepoint_level()); | 
 |   ASSERT(!T->IsSafepointRequestedLocked(T->current_safepoint_level())); | 
 | } | 
 |  | 
 | SafepointLevel SafepointHandler::InnermostSafepointOperation( | 
 |     const Thread* current_thread) const { | 
 |   // The [current_thread] may not own the active safepoint. | 
 |   intptr_t last_count = -1; | 
 |   SafepointLevel last_level = SafepointLevel::kNoSafepoint; | 
 |  | 
 |   // Notice: We access SafepointLevel::{owner_,operation_count_} fields | 
 |   // without lock. This is ok since: | 
 |   // | 
 |   //   * If the current thread is the owner, then it will be the one that has | 
 |   //     last written `Thread::Current()` to the `owner_` field (as well as | 
 |   //     updated the `operation_count_`) - nobody else can update those fields | 
 |   //     in the meantime. Once the current thread exits we set it to `nullptr`. | 
 |   // | 
 |   //   * If there's no owner or another thread is the owner the value cannot be | 
 |   //     `Thread::Current()`: only our thread can write that particular value. | 
 |   // | 
 |   //   => Even if there's racy writes by another thread, the logic is still | 
 |   //      safe. | 
 |   // | 
 |   for (intptr_t level = 0; level < SafepointLevel::kNumLevels; ++level) { | 
 |     if (handlers_[level]->owner_ == current_thread) { | 
 |       const intptr_t count = handlers_[level]->operation_count_; | 
 |       if (count < last_count) return last_level; | 
 |       last_count = count; | 
 |       last_level = static_cast<SafepointLevel>(level); | 
 |     } else { | 
 |       return last_level; | 
 |     } | 
 |   } | 
 |   return last_level; | 
 | } | 
 |  | 
 | void SafepointHandler::BlockForSafepoint(Thread* T) { | 
 |   ASSERT(!T->BypassSafepoints()); | 
 |   MonitorLocker tl(T->thread_lock()); | 
 |   // This takes into account the safepoint level the thread can participate in. | 
 |   const SafepointLevel level = T->current_safepoint_level(); | 
 |   if (T->IsSafepointRequestedLocked(level)) { | 
 |     EnterSafepointLocked(T, &tl, level); | 
 |     ExitSafepointLocked(T, &tl, level); | 
 |     ASSERT(!T->IsSafepointRequestedLocked(level)); | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::EnterSafepointLocked(Thread* T, | 
 |                                             MonitorLocker* tl, | 
 |                                             SafepointLevel level) { | 
 |   T->SetAtSafepoint(true, level); | 
 |   // Several safepointing operations (at different) levels may happen at same | 
 |   // time. Ensure we notify all of them that we are parked now. | 
 |   for (intptr_t i = 0; i <= level; ++i) { | 
 |     if (T->IsSafepointLevelRequestedLocked(static_cast<SafepointLevel>(i))) { | 
 |       handlers_[i]->NotifyWeAreParked(T); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::LevelHandler::NotifyWeAreParked(Thread* T) { | 
 |   ASSERT(owner_ != nullptr); | 
 |   MonitorLocker sl(&parked_lock_); | 
 |   ASSERT(num_threads_not_parked_ > 0); | 
 |   num_threads_not_parked_ -= 1; | 
 |   if (num_threads_not_parked_ == 0) { | 
 |     sl.Notify(); | 
 |   } | 
 | } | 
 |  | 
 | void SafepointHandler::ExitSafepointLocked(Thread* T, | 
 |                                            MonitorLocker* tl, | 
 |                                            SafepointLevel level) { | 
 |   while (T->IsSafepointRequestedLocked(level)) { | 
 |     T->SetBlockedForSafepoint(true); | 
 |     tl->Wait(); | 
 |     T->SetBlockedForSafepoint(false); | 
 |   } | 
 |   T->SetAtSafepoint(false, level); | 
 | } | 
 |  | 
 | }  // namespace dart |