[vm/isolates] Put limit on how many isolates are suspended.

Fixes https://github.com/dart-lang/sdk/issues/52154
TEST=ci

Change-Id: I6f620113232cf8f771dd73be334ace0e385784f5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/297920
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
Alexander Aprelev 2023-04-26 22:18:51 +00:00 committed by Commit Queue
parent 97a762b083
commit 86d754690a
7 changed files with 45 additions and 9 deletions

View file

@ -118,7 +118,7 @@ void FieldTable::Grow(intptr_t new_capacity) {
// Ensure that new_table_ is populated before it is published
// via store to table_.
reinterpret_cast<AcqRelAtomic<ObjectPtr*>*>(&table_)->store(new_table);
if (isolate_ != nullptr) {
if (isolate_ != nullptr && isolate_->mutator_thread() != nullptr) {
isolate_->mutator_thread()->field_table_values_ = table_;
}
}

View file

@ -58,6 +58,9 @@ void WeakCodeReferences::DisableCode(bool are_mutators_stopped) {
isolate_group->ForEachIsolate(
[&](Isolate* isolate) {
auto mutator_thread = isolate->mutator_thread();
if (mutator_thread == nullptr) {
return;
}
DartFrameIterator iterator(
mutator_thread, StackFrameIterator::kAllowCrossThreadIteration);
StackFrame* frame = iterator.NextFrame();

View file

@ -1420,6 +1420,9 @@ void ProgramReloadContext::EnsuredUnoptimizedCodeForStack() {
IG->ForEachIsolate([](Isolate* isolate) {
auto thread = isolate->mutator_thread();
if (thread == nullptr) {
return;
}
StackFrameIterator it(ValidationPolicy::kDontValidateFrames, thread,
StackFrameIterator::kAllowCrossThreadIteration);
@ -1996,6 +1999,9 @@ void ProgramReloadContext::ResetUnoptimizedICsOnStack() {
CallSiteResetter resetter(zone);
IG->ForEachIsolate([&](Isolate* isolate) {
if (isolate->mutator_thread() == nullptr) {
return;
}
DartFrameIterator iterator(isolate->mutator_thread(),
StackFrameIterator::kAllowCrossThreadIteration);
StackFrame* frame = iterator.NextFrame();

View file

@ -3307,6 +3307,9 @@ void DeoptimizeFunctionsOnStack() {
isolate_group->ForEachIsolate(
[&](Isolate* isolate) {
auto mutator_thread = isolate->mutator_thread();
if (mutator_thread == nullptr) {
return;
}
DartFrameIterator iterator(
mutator_thread, StackFrameIterator::kAllowCrossThreadIteration);
StackFrame* frame = iterator.NextFrame();
@ -3332,6 +3335,9 @@ static void DeoptimizeLastDartFrameIfOptimized() {
auto isolate_group = thread->isolate_group();
isolate_group->RunWithStoppedMutators([&]() {
auto mutator_thread = isolate->mutator_thread();
if (mutator_thread == nullptr) {
return;
}
DartFrameIterator iterator(mutator_thread,
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* frame = iterator.NextFrame();

View file

@ -412,6 +412,22 @@ bool Thread::EnterIsolate(Isolate* isolate) {
return true;
}
static bool ShouldSuspend(bool isolate_shutdown, Thread* thread) {
// Must destroy thread.
if (isolate_shutdown) return false;
// Must retain thread.
if (thread->HasActiveState() || thread->OwnsSafepoint()) return true;
// Could do either. When there are few isolates suspend to avoid work
// entering and leaving. When there are many isolate, destroy the thread to
// avoid the root set growing too big.
const intptr_t kMaxSuspendedThreads = 20;
auto group = thread->isolate_group();
return group->thread_registry()->active_isolates_count() <
kMaxSuspendedThreads;
}
void Thread::ExitIsolate(bool isolate_shutdown) {
Thread* thread = Thread::Current();
ASSERT(thread != nullptr);
@ -438,11 +454,8 @@ void Thread::ExitIsolate(bool isolate_shutdown) {
// makes entering/exiting quite fast as it mainly boils down to safepoint
// transitions. Though any operation that walks over all active threads will
// see this thread as well (e.g. safepoint operations).
//
// We could safely use `suspend = !HasActiveState()` here instead and may
// consider doing so e.g. if the number of isolates is very large.
const bool suspend = !isolate_shutdown;
if (suspend) {
const bool is_nested_exit = thread->top_exit_frame_info() != 0;
if (ShouldSuspend(isolate_shutdown, thread)) {
const auto tag =
isolate->is_runnable() ? VMTag::kIdleTagId : VMTag::kLoadWaitTagId;
SuspendThreadInternal(thread, tag);
@ -462,7 +475,6 @@ void Thread::ExitIsolate(bool isolate_shutdown) {
// To let VM's thread pool (if we run on it) know that this thread is
// occupying a mutator again (decreases its max size).
const bool is_nested_exit = thread->top_exit_frame_info() != 0;
ASSERT(!(isolate_shutdown && is_nested_exit));
group->DecreaseMutatorCount(isolate, is_nested_exit);
}

View file

@ -110,6 +110,7 @@ void ThreadRegistry::AddToActiveListLocked(Thread* thread) {
ASSERT(threads_lock()->IsOwnedByCurrentThread());
thread->next_ = active_list_;
active_list_ = thread;
active_isolates_count_.fetch_add(1);
}
void ThreadRegistry::RemoveFromActiveListLocked(Thread* thread) {
@ -124,6 +125,7 @@ void ThreadRegistry::RemoveFromActiveListLocked(Thread* thread) {
} else {
prev->next_ = current->next_;
}
active_isolates_count_.fetch_sub(1);
break;
}
prev = current;

View file

@ -19,11 +19,14 @@ class JSONStream;
class JSONArray;
#endif
// Unordered collection of threads relating to a particular isolate.
// Unordered collection of threads relating to a particular isolate group.
class ThreadRegistry {
public:
ThreadRegistry()
: threads_lock_(), active_list_(nullptr), free_list_(nullptr) {}
: threads_lock_(),
active_list_(nullptr),
free_list_(nullptr),
active_isolates_count_(0) {}
~ThreadRegistry();
void VisitObjectPointers(IsolateGroup* isolate_group_of_interest,
@ -35,6 +38,9 @@ class ThreadRegistry {
void AcquireMarkingStacks();
void ReleaseMarkingStacks();
// Concurrent-approximate number of active isolates in the active_list
intptr_t active_isolates_count() { return active_isolates_count_.load(); }
Monitor* threads_lock() const { return &threads_lock_; }
#ifndef PRODUCT
@ -56,6 +62,7 @@ class ThreadRegistry {
mutable Monitor threads_lock_;
Thread* active_list_; // List of active threads in the isolate.
Thread* free_list_; // Free list of Thread objects that can be reused.
RelaxedAtomic<intptr_t> active_isolates_count_;
friend class Thread;
friend class SafepointHandler;