1cbf6c31e5
This is the idle equivalent of 3451c348db.
Change-Id: Ica6e0595c39257540ea731355868a107de2c2597
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149960
Reviewed-by: Siva Annamalai <asiva@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
250 lines
8.0 KiB
C++
250 lines
8.0 KiB
C++
// 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)
|
|
: ThreadStackResource(T) {
|
|
ASSERT(T != nullptr && T->isolate_group() != nullptr);
|
|
|
|
SafepointHandler* handler = T->isolate_group()->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 != nullptr && T->isolate_group() != nullptr);
|
|
|
|
// Resume all threads which are blocked for the safepoint operation.
|
|
SafepointHandler* handler = T->isolate_group()->safepoint_handler();
|
|
ASSERT(handler != NULL);
|
|
handler->ResumeThreads(T);
|
|
}
|
|
|
|
ForceGrowthSafepointOperationScope::ForceGrowthSafepointOperationScope(
|
|
Thread* T)
|
|
: ThreadStackResource(T) {
|
|
ASSERT(T != NULL);
|
|
IsolateGroup* IG = T->isolate_group();
|
|
ASSERT(IG != NULL);
|
|
|
|
SafepointHandler* handler = IG->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);
|
|
|
|
// N.B.: Change growth policy inside the safepoint to prevent racy access.
|
|
Heap* heap = IG->heap();
|
|
current_growth_controller_state_ = heap->GrowthControlState();
|
|
heap->DisableGrowthControl();
|
|
}
|
|
|
|
ForceGrowthSafepointOperationScope::~ForceGrowthSafepointOperationScope() {
|
|
Thread* T = thread();
|
|
ASSERT(T != NULL);
|
|
IsolateGroup* IG = T->isolate_group();
|
|
ASSERT(IG != NULL);
|
|
|
|
// N.B.: Change growth policy inside the safepoint to prevent racy access.
|
|
Heap* heap = IG->heap();
|
|
heap->SetGrowthControlState(current_growth_controller_state_);
|
|
|
|
// Resume all threads which are blocked for the safepoint operation.
|
|
SafepointHandler* handler = IG->safepoint_handler();
|
|
ASSERT(handler != NULL);
|
|
handler->ResumeThreads(T);
|
|
|
|
if (current_growth_controller_state_) {
|
|
ASSERT(T->CanCollectGarbage());
|
|
// Check if we passed the growth limit during the scope.
|
|
if (heap->old_space()->ReachedHardThreshold()) {
|
|
heap->CollectGarbage(Heap::kMarkSweep, Heap::kOldSpace);
|
|
} else {
|
|
heap->CheckStartConcurrentMarking(T, Heap::kOldSpace);
|
|
}
|
|
}
|
|
}
|
|
|
|
SafepointHandler::SafepointHandler(IsolateGroup* isolate_group)
|
|
: isolate_group_(isolate_group),
|
|
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_group_ = 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_group()->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()) {
|
|
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_group()->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_group()->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
|