[vm/concurrency] Make all isolates within an isolate group coordinate on GCs

This CL moves the thread registry and the safepoint handler to the
[IsolateGroup]. This will cause all threads belonging to the isolate
group to safepoint together.

  => We will therefore start to get an idea of the impact this will have on
  pause times.

So far it was only possible to enter a particular isolate (e.g. as mutator
thread or auxiliary thread). This CL adds support for entering an isolate
group (instead of a particular isolate) as an auxiliarly thread. The
current isolate group is available via `IsolateGroup::Current()`.

  => This is a preparation step to let GC threads enter the isolate
     group as auxiliary threads, not associated with a particular isolate but
     to an isolate group as a whole.

Issue https://github.com/dart-lang/sdk/issues/36097

Change-Id: I7069d07130938d370869f02060570143bfeb1b48
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108801
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann 2019-07-23 10:58:11 +00:00 committed by commit-bot@chromium.org
parent 9ec6a48613
commit 0a2993687b
10 changed files with 333 additions and 177 deletions

View file

@ -17,7 +17,7 @@ SafepointOperationScope::SafepointOperationScope(Thread* T)
Isolate* I = T->isolate();
ASSERT(I != NULL);
SafepointHandler* handler = I->safepoint_handler();
SafepointHandler* handler = I->group()->safepoint_handler();
ASSERT(handler != NULL);
// Signal all threads to get to a safepoint and wait for them to
@ -37,8 +37,8 @@ SafepointOperationScope::~SafepointOperationScope() {
handler->ResumeThreads(T);
}
SafepointHandler::SafepointHandler(Isolate* isolate)
: isolate_(isolate),
SafepointHandler::SafepointHandler(IsolateGroup* isolate_group)
: isolate_group_(isolate_group),
safepoint_lock_(),
number_threads_not_at_safepoint_(0),
safepoint_operation_count_(0),
@ -47,7 +47,7 @@ SafepointHandler::SafepointHandler(Isolate* isolate)
SafepointHandler::~SafepointHandler() {
ASSERT(owner_ == NULL);
ASSERT(safepoint_operation_count_ == 0);
isolate_ = NULL;
isolate_group_ = NULL;
}
void SafepointHandler::SafepointThreads(Thread* T) {
@ -79,7 +79,7 @@ void SafepointHandler::SafepointThreads(Thread* 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();
Thread* current = isolate_group()->thread_registry()->active_list();
while (current != NULL) {
MonitorLocker tl(current->thread_lock());
if (!current->BypassSafepoints()) {
@ -113,7 +113,8 @@ void SafepointHandler::SafepointThreads(Thread* T) {
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();
for (Thread* current =
isolate_group()->thread_registry()->active_list();
current != NULL; current = current->next()) {
if (!current->IsAtSafepoint()) {
OS::PrintErr("Attempt:%" Pd
@ -139,7 +140,7 @@ void SafepointHandler::ResumeThreads(Thread* T) {
decrement_safepoint_operation_count();
return;
}
Thread* current = isolate()->thread_registry()->active_list();
Thread* current = isolate_group()->thread_registry()->active_list();
while (current != NULL) {
MonitorLocker tl(current->thread_lock());
if (!current->BypassSafepoints()) {

View file

@ -24,10 +24,11 @@ class SafepointOperationScope : public ThreadStackResource {
DISALLOW_COPY_AND_ASSIGN(SafepointOperationScope);
};
// Implements handling of safepoint operations for all threads in an Isolate.
// Implements handling of safepoint operations for all threads in an
// IsolateGroup.
class SafepointHandler {
public:
explicit SafepointHandler(Isolate* I);
explicit SafepointHandler(IsolateGroup* I);
~SafepointHandler();
void EnterSafepointUsingLock(Thread* T);
@ -39,8 +40,8 @@ class SafepointHandler {
void SafepointThreads(Thread* T);
void ResumeThreads(Thread* T);
Isolate* isolate() const { return isolate_; }
Monitor* threads_lock() const { return isolate_->threads_lock(); }
IsolateGroup* isolate_group() const { return isolate_group_; }
Monitor* threads_lock() const { return isolate_group_->threads_lock(); }
bool SafepointInProgress() const {
ASSERT(threads_lock()->IsOwnedByCurrentThread());
return ((safepoint_operation_count_ > 0) && (owner_ != NULL));
@ -74,7 +75,7 @@ class SafepointHandler {
safepoint_operation_count_ -= 1;
}
Isolate* isolate_;
IsolateGroup* isolate_group_;
// Monitor used by thread initiating a safepoint operation to track threads
// not at a safepoint and wait for these threads to reach a safepoint.
@ -91,6 +92,7 @@ class SafepointHandler {
Thread* owner_;
friend class Isolate;
friend class IsolateGroup;
friend class SafepointOperationScope;
friend class HeapIterationScope;
};

View file

@ -909,8 +909,14 @@ void Scavenger::MakeNewSpaceIterable() const {
MonitorLocker ml(isolate->threads_lock(), false);
Thread* current = heap_->isolate()->thread_registry()->active_list();
while (current != NULL) {
if (current->HasActiveTLAB()) {
heap_->MakeTLABIterable(current);
// NOTE: During the transition period all isolates within an isolate group
// share the thread registry, but have their own heap.
// So we explicitly filter those threads which belong to the isolate of
// interest (once we have a shared heap this needs to change).
if (current->isolate() == isolate) {
if (current->HasActiveTLAB()) {
heap_->MakeTLABIterable(current);
}
}
current = current->next();
}
@ -925,7 +931,13 @@ void Scavenger::AbandonTLABs(Isolate* isolate) {
MonitorLocker ml(isolate->threads_lock(), false);
Thread* current = isolate->thread_registry()->active_list();
while (current != NULL) {
heap_->AbandonRemainingTLAB(current);
// NOTE: During the transition period all isolates within an isolate group
// share the thread registry, but have their own heap.
// So we explicitly filter those threads which belong to the isolate of
// interest (once we have a shared heap this needs to change).
if (current->isolate() == isolate) {
heap_->AbandonRemainingTLAB(current);
}
current = current->next();
}
Thread* mutator_thread = isolate->mutator_thread();

View file

@ -134,6 +134,8 @@ IsolateGroup::IsolateGroup(std::unique_ptr<IsolateGroupSource> source,
void* embedder_data)
: source_(std::move(source)),
embedder_data_(embedder_data),
thread_registry_(new ThreadRegistry()),
safepoint_handler_(new SafepointHandler(this)),
isolates_monitor_(new Monitor()),
isolates_() {}
@ -167,6 +169,145 @@ void IsolateGroup::UnregisterIsolate(Isolate* isolate) {
}
}
Thread* IsolateGroup::ScheduleThreadLocked(MonitorLocker* ml,
Thread* existing_mutator_thread,
bool is_vm_isolate,
bool is_mutator,
bool bypass_safepoint) {
ASSERT(threads_lock()->IsOwnedByCurrentThread());
// Schedule the thread into the isolate group by associating
// a 'Thread' structure with it (this is done while we are holding
// the thread registry lock).
Thread* thread = nullptr;
OSThread* os_thread = OSThread::Current();
if (os_thread != nullptr) {
// If a safepoint operation is in progress wait for it
// to finish before scheduling this thread in.
while (!bypass_safepoint && safepoint_handler()->SafepointInProgress()) {
ml->Wait();
}
if (is_mutator) {
if (existing_mutator_thread == nullptr) {
// Allocate a new [Thread] structure for the mutator thread.
thread = thread_registry()->GetFreeThreadLocked(is_vm_isolate);
} else {
// Reuse the existing cached [Thread] structure for the mutator thread.,
// see comment in 'base_isolate.h'.
thread_registry()->AddToActiveListLocked(existing_mutator_thread);
thread = existing_mutator_thread;
}
} else {
thread = thread_registry()->GetFreeThreadLocked(is_vm_isolate);
}
// Now get a free Thread structure.
ASSERT(thread != nullptr);
thread->ResetHighWatermark();
// Set up other values and set the TLS value.
thread->isolate_ = nullptr;
thread->isolate_group_ = this;
thread->set_os_thread(os_thread);
ASSERT(thread->execution_state() == Thread::kThreadInNative);
thread->set_execution_state(Thread::kThreadInVM);
thread->set_safepoint_state(
Thread::SetBypassSafepoints(bypass_safepoint, 0));
thread->set_vm_tag(VMTag::kVMTagId);
ASSERT(thread->no_safepoint_scope_depth() == 0);
os_thread->set_thread(thread);
Thread::SetCurrent(thread);
os_thread->EnableThreadInterrupts();
}
return thread;
}
void IsolateGroup::UnscheduleThreadLocked(MonitorLocker* ml,
Thread* thread,
bool is_mutator,
bool bypass_safepoint) {
// Disassociate the 'Thread' structure and unschedule the thread
// from this isolate group.
if (!is_mutator) {
ASSERT(thread->api_top_scope_ == nullptr);
ASSERT(thread->zone() == nullptr);
ASSERT(thread->sticky_error() == Error::null());
}
if (!bypass_safepoint) {
// Ensure that the thread reports itself as being at a safepoint.
thread->EnterSafepoint();
}
OSThread* os_thread = thread->os_thread();
ASSERT(os_thread != nullptr);
os_thread->DisableThreadInterrupts();
os_thread->set_thread(nullptr);
OSThread::SetCurrent(os_thread);
// Even if we unschedule the mutator thread, e.g. via calling
// `Dart_ExitIsolate()` inside a native, we might still have one or more Dart
// stacks active, which e.g. GC marker threads want to visit. So we don't
// clear out the isolate pointer if we are on the mutator thread.
//
// The [thread] structure for the mutator thread is kept alive in the thread
// registry even if the mutator thread is temporarily unscheduled.
//
// All other threads are not allowed to unschedule themselves and schedule
// again later on.
if (!is_mutator) {
thread->isolate_ = nullptr;
}
thread->heap_ = nullptr;
thread->set_os_thread(nullptr);
thread->set_execution_state(Thread::kThreadInNative);
thread->set_safepoint_state(Thread::SetAtSafepoint(true, 0));
thread->clear_pending_functions();
ASSERT(thread->no_safepoint_scope_depth() == 0);
if (is_mutator) {
// The mutator thread structure stays alive and attached to the isolate as
// long as the isolate lives. So we simply remove the thread from the list
// of scheduled threads.
thread_registry()->RemoveFromActiveListLocked(thread);
} else {
// Return thread structure.
thread_registry()->ReturnThreadLocked(thread);
}
}
Thread* IsolateGroup::ScheduleThread(bool bypass_safepoint) {
// We are about to associate the thread with an isolate group and it would
// not be possible to correctly track no_safepoint_scope_depth for the
// thread in the constructor/destructor of MonitorLocker,
// so we create a MonitorLocker object which does not do any
// no_safepoint_scope_depth increments/decrements.
MonitorLocker ml(threads_lock(), false);
const bool is_vm_isolate = false;
// Schedule the thread into the isolate by associating
// a 'Thread' structure with it (this is done while we are holding
// the thread registry lock).
return ScheduleThreadLocked(&ml, /*existing_mutator_thread=*/nullptr,
is_vm_isolate, /*is_mutator=*/false,
bypass_safepoint);
}
void IsolateGroup::UnscheduleThread(Thread* thread,
bool is_mutator,
bool bypass_safepoint) {
// Disassociate the 'Thread' structure and unschedule the thread
// from this isolate group.
//
// We are disassociating the thread from an isolate and it would
// not be possible to correctly track no_safepoint_scope_depth for the
// thread in the constructor/destructor of MonitorLocker,
// so we create a MonitorLocker object which does not do any
// no_safepoint_scope_depth increments/decrements.
MonitorLocker ml(threads_lock(), false);
UnscheduleThreadLocked(&ml, thread, is_mutator, bypass_safepoint);
}
bool IsolateVisitor::IsVMInternalIsolate(Isolate* isolate) const {
return Isolate::IsVMInternalIsolate(isolate);
}
@ -924,8 +1065,6 @@ Isolate::Isolate(IsolateGroup* isolate_group,
last_reload_timestamp_(OS::GetCurrentTimeMillis()),
#endif // !defined(PRODUCT)
start_time_micros_(OS::GetCurrentMonotonicMicros()),
thread_registry_(new ThreadRegistry()),
safepoint_handler_(new SafepointHandler(this)),
random_(),
mutex_(NOT_IN_PRODUCT("Isolate::mutex_")),
symbols_mutex_(NOT_IN_PRODUCT("Isolate::symbols_mutex_")),
@ -1024,7 +1163,6 @@ Isolate::~Isolate() {
ASSERT(deopt_context_ ==
nullptr); // No deopt in progress when isolate deleted.
ASSERT(spawn_count_ == 0);
delete safepoint_handler_;
// We have cached the mutator thread, delete it.
ASSERT(scheduled_mutator_thread_ == nullptr);
@ -1032,8 +1170,6 @@ Isolate::~Isolate() {
delete mutator_thread_;
mutator_thread_ = nullptr;
delete thread_registry_;
if (obfuscation_map_ != nullptr) {
for (intptr_t i = 0; obfuscation_map_[i] != nullptr; i++) {
delete[] obfuscation_map_[i];
@ -2061,7 +2197,7 @@ void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor,
void Isolate::VisitStackPointers(ObjectPointerVisitor* visitor,
ValidationPolicy validate_frames) {
// Visit objects in all threads (e.g., Dart stack, handles in zones).
thread_registry()->VisitObjectPointers(visitor, validate_frames);
thread_registry()->VisitObjectPointers(this, visitor, validate_frames);
// Visit mutator thread, even if the isolate isn't entered/scheduled (there
// might be live API handles to visit).
@ -2077,7 +2213,7 @@ void Isolate::VisitWeakPersistentHandles(HandleVisitor* visitor) {
}
void Isolate::ReleaseStoreBuffers() {
thread_registry()->ReleaseStoreBuffers();
thread_registry()->ReleaseStoreBuffers(this);
}
void Isolate::EnableIncrementalBarrier(MarkingStack* marking_stack,
@ -2085,12 +2221,12 @@ void Isolate::EnableIncrementalBarrier(MarkingStack* marking_stack,
ASSERT(marking_stack_ == nullptr);
marking_stack_ = marking_stack;
deferred_marking_stack_ = deferred_marking_stack;
thread_registry()->AcquireMarkingStacks();
thread_registry()->AcquireMarkingStacks(this);
ASSERT(Thread::Current()->is_marking());
}
void Isolate::DisableIncrementalBarrier() {
thread_registry()->ReleaseMarkingStacks();
thread_registry()->ReleaseMarkingStacks(this);
ASSERT(marking_stack_ != nullptr);
marking_stack_ = nullptr;
deferred_marking_stack_ = nullptr;
@ -2255,8 +2391,8 @@ void Isolate::PrintJSON(JSONStream* stream, bool ref) {
jsobj.AddProperty("rootLib", lib);
}
intptr_t zone_handle_count = thread_registry_->CountZoneHandles();
intptr_t scoped_handle_count = thread_registry_->CountScopedHandles();
intptr_t zone_handle_count = thread_registry()->CountZoneHandles(this);
intptr_t scoped_handle_count = thread_registry()->CountScopedHandles(this);
jsobj.AddProperty("_numZoneHandles", zone_handle_count);
jsobj.AddProperty("_numScopedHandles", scoped_handle_count);
@ -2320,7 +2456,7 @@ void Isolate::PrintJSON(JSONStream* stream, bool ref) {
}
}
jsobj.AddProperty("_threads", thread_registry_);
jsobj.AddProperty("_threads", thread_registry());
}
void Isolate::PrintMemoryUsageJSON(JSONStream* stream) {
@ -2875,76 +3011,47 @@ void Isolate::WaitForOutstandingSpawns() {
}
}
Monitor* Isolate::threads_lock() const {
Monitor* IsolateGroup::threads_lock() const {
return thread_registry_->threads_lock();
}
Thread* Isolate::ScheduleThread(bool is_mutator, bool bypass_safepoint) {
// Schedule the thread into the isolate by associating
// a 'Thread' structure with it (this is done while we are holding
// the thread registry lock).
Thread* thread = nullptr;
OSThread* os_thread = OSThread::Current();
if (os_thread != nullptr) {
// We are about to associate the thread with an isolate and it would
// not be possible to correctly track no_safepoint_scope_depth for the
// thread in the constructor/destructor of MonitorLocker,
// so we create a MonitorLocker object which does not do any
// no_safepoint_scope_depth increments/decrements.
MonitorLocker ml(threads_lock(), false);
// We are about to associate the thread with an isolate group and it would
// not be possible to correctly track no_safepoint_scope_depth for the
// thread in the constructor/destructor of MonitorLocker,
// so we create a MonitorLocker object which does not do any
// no_safepoint_scope_depth increments/decrements.
MonitorLocker ml(group()->threads_lock(), false);
// Check to make sure we don't already have a mutator thread.
if (is_mutator && scheduled_mutator_thread_ != nullptr) {
return nullptr;
}
// If a safepoint operation is in progress wait for it
// to finish before scheduling this thread in.
while (!bypass_safepoint && safepoint_handler()->SafepointInProgress()) {
ml.Wait();
}
// NOTE: We cannot just use `Dart::vm_isolate() == this` here, since during
// VM startup it might not have been set at this point.
const bool is_vm_isolate =
Dart::vm_isolate() == nullptr || Dart::vm_isolate() == this;
if (is_mutator) {
if (mutator_thread_ == nullptr) {
// Allocate a new [Thread] structure for the mutator thread.
thread = thread_registry()->GetFreeThreadLocked(is_vm_isolate);
mutator_thread_ = thread;
} else {
// Reuse the existing cached [Thread] structure for the mutator thread.,
// see comment in 'base_isolate.h'.
thread_registry()->AddToActiveListLocked(mutator_thread_);
thread = mutator_thread_;
}
} else {
thread = thread_registry()->GetFreeThreadLocked(is_vm_isolate);
}
ASSERT(thread != nullptr);
thread->ResetHighWatermark();
// Set up other values and set the TLS value.
thread->isolate_ = this;
ASSERT(heap() != nullptr);
thread->heap_ = heap();
thread->set_os_thread(os_thread);
ASSERT(thread->execution_state() == Thread::kThreadInNative);
thread->set_execution_state(Thread::kThreadInVM);
thread->set_safepoint_state(
Thread::SetBypassSafepoints(bypass_safepoint, 0));
thread->set_vm_tag(VMTag::kVMTagId);
ASSERT(thread->no_safepoint_scope_depth() == 0);
os_thread->set_thread(thread);
if (is_mutator) {
scheduled_mutator_thread_ = thread;
}
Thread::SetCurrent(thread);
os_thread->EnableThreadInterrupts();
// Check to make sure we don't already have a mutator thread.
if (is_mutator && scheduled_mutator_thread_ != nullptr) {
return nullptr;
}
// NOTE: We cannot just use `Dart::vm_isolate() == this` here, since during
// VM startup it might not have been set at this point.
const bool is_vm_isolate =
Dart::vm_isolate() == nullptr || Dart::vm_isolate() == this;
// We lazily create a [Thread] structure for the mutator thread, but we'll
// reuse it until the death of the isolate.
Thread* existing_mutator_thread = is_mutator ? mutator_thread_ : nullptr;
// Schedule the thread into the isolate by associating a 'Thread' structure
// with it (this is done while we are holding the thread registry lock).
Thread* thread =
group()->ScheduleThreadLocked(&ml, existing_mutator_thread, is_vm_isolate,
is_mutator, bypass_safepoint);
if (is_mutator) {
ASSERT(mutator_thread_ == nullptr || mutator_thread_ == thread);
mutator_thread_ = thread;
scheduled_mutator_thread_ = thread;
}
thread->isolate_ = this;
ASSERT(heap() != nullptr);
thread->heap_ = heap();
return thread;
}
@ -2958,57 +3065,18 @@ void Isolate::UnscheduleThread(Thread* thread,
// thread in the constructor/destructor of MonitorLocker,
// so we create a MonitorLocker object which does not do any
// no_safepoint_scope_depth increments/decrements.
MonitorLocker ml(threads_lock(), false);
MonitorLocker ml(group()->threads_lock(), false);
if (is_mutator) {
if (thread->sticky_error() != Error::null()) {
ASSERT(sticky_error_ == Error::null());
sticky_error_ = thread->StealStickyError();
}
} else {
ASSERT(thread->api_top_scope_ == nullptr);
ASSERT(thread->zone() == nullptr);
ASSERT(thread->sticky_error() == Error::null());
}
if (!bypass_safepoint) {
// Ensure that the thread reports itself as being at a safepoint.
thread->EnterSafepoint();
}
OSThread* os_thread = thread->os_thread();
ASSERT(os_thread != nullptr);
os_thread->DisableThreadInterrupts();
os_thread->set_thread(nullptr);
OSThread::SetCurrent(os_thread);
// Even if we unschedule the mutator thread, e.g. via calling
// `Dart_ExitIsolate()` inside a native, we might still have one or more Dart
// stacks active, which e.g. GC marker threads want to visit. So we don't
// clear out the isolate pointer if we are on the mutator thread.
//
// The [thread] structure for the mutator thread is kept alive in the thread
// registry even if the mutator thread is temporarily unscheduled.
//
// All other threads are not allowed to unschedule themselves and schedule
// again later on.
if (!is_mutator) {
thread->isolate_ = nullptr;
}
thread->heap_ = nullptr;
thread->set_os_thread(nullptr);
thread->set_execution_state(Thread::kThreadInNative);
thread->set_safepoint_state(Thread::SetAtSafepoint(true, 0));
thread->clear_pending_functions();
ASSERT(thread->no_safepoint_scope_depth() == 0);
if (is_mutator) {
ASSERT(mutator_thread_ == thread);
ASSERT(mutator_thread_ == scheduled_mutator_thread_);
thread_registry()->RemoveFromActiveListLocked(thread);
scheduled_mutator_thread_ = nullptr;
} else {
// Return thread structure.
thread_registry()->ReturnThreadLocked(thread);
}
group()->UnscheduleThreadLocked(&ml, thread, is_mutator, bypass_safepoint);
}
static const char* NewConstChar(const char* chars) {

View file

@ -56,6 +56,7 @@ class IsolateSpawnState;
class Log;
class Message;
class MessageHandler;
class MonitorLocker;
class Mutex;
class Object;
class ObjectIdRing;
@ -227,9 +228,35 @@ class IsolateGroup {
void RegisterIsolate(Isolate* isolate);
void UnregisterIsolate(Isolate* isolate);
Monitor* threads_lock() const;
ThreadRegistry* thread_registry() const { return thread_registry_.get(); }
SafepointHandler* safepoint_handler() { return safepoint_handler_.get(); }
static inline IsolateGroup* Current() {
Thread* thread = Thread::Current();
return thread == nullptr ? nullptr : thread->isolate_group();
}
Thread* ScheduleThreadLocked(MonitorLocker* ml,
Thread* existing_mutator_thread,
bool is_vm_isolate,
bool is_mutator,
bool bypass_safepoint = false);
void UnscheduleThreadLocked(MonitorLocker* ml,
Thread* thread,
bool is_mutator,
bool bypass_safepoint = false);
Thread* ScheduleThread(bool bypass_safepoint = false);
void UnscheduleThread(Thread* thread,
bool is_mutator,
bool bypass_safepoint = false);
private:
std::unique_ptr<IsolateGroupSource> source_;
void* embedder_data_ = nullptr;
std::unique_ptr<ThreadRegistry> thread_registry_;
std::unique_ptr<SafepointHandler> safepoint_handler_;
std::unique_ptr<Monitor> isolates_monitor_;
IntrusiveDList<Isolate> isolates_;
intptr_t isolate_count_ = 0;
@ -297,8 +324,11 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
return deferred_marking_stack_;
}
ThreadRegistry* thread_registry() const { return thread_registry_; }
SafepointHandler* safepoint_handler() const { return safepoint_handler_; }
ThreadRegistry* thread_registry() const { return group()->thread_registry(); }
SafepointHandler* safepoint_handler() const {
return group()->safepoint_handler();
}
ClassTable* class_table() { return &class_table_; }
static intptr_t class_table_offset() {
return OFFSET_OF(Isolate, class_table_);
@ -958,7 +988,7 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
const GrowableObjectArray& value);
#endif // !defined(PRODUCT)
Monitor* threads_lock() const;
Monitor* threads_lock() { return isolate_group_->threads_lock(); }
Thread* ScheduleThread(bool is_mutator, bool bypass_safepoint = false);
void UnscheduleThread(Thread* thread,
bool is_mutator,
@ -1088,8 +1118,6 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
// All other fields go here.
int64_t start_time_micros_;
ThreadRegistry* thread_registry_;
SafepointHandler* safepoint_handler_;
Dart_MessageNotifyCallback message_notify_callback_ = nullptr;
char* name_ = nullptr;
Dart_Port main_port_ = 0;

View file

@ -2,9 +2,12 @@
// 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 <utility>
#include "vm/message_handler.h"
#include "vm/dart.h"
#include "vm/heap/safepoint.h"
#include "vm/lockers.h"
#include "vm/object.h"
#include "vm/object_store.h"
@ -114,8 +117,9 @@ void MessageHandler::Run(ThreadPool* pool,
start_callback_ = start_callback;
end_callback_ = end_callback;
callback_data_ = data;
task_running_ = pool_->Run<MessageHandlerTask>(this);
ASSERT(task_running_);
task_running_ = true;
const bool launched_successfully = pool_->Run<MessageHandlerTask>(this);
ASSERT(launched_successfully);
}
void MessageHandler::PostMessage(std::unique_ptr<Message> message,
@ -155,10 +159,11 @@ void MessageHandler::PostMessage(std::unique_ptr<Message> message,
ml.Notify();
}
if ((pool_ != NULL) && !task_running_) {
if (pool_ != nullptr && !task_running_) {
ASSERT(!delete_me_);
task_running_ = pool_->Run<MessageHandlerTask>(this);
ASSERT(task_running_);
task_running_ = true;
const bool launched_successfully = pool_->Run<MessageHandlerTask>(this);
ASSERT(launched_successfully);
}
}
@ -184,10 +189,18 @@ MessageHandler::MessageStatus MessageHandler::HandleMessages(
MonitorLocker* ml,
bool allow_normal_messages,
bool allow_multiple_normal_messages) {
// TODO(turnidge): Add assert that monitor_ is held here.
ASSERT(monitor_.IsOwnedByCurrentThread());
// If isolate() returns NULL StartIsolateScope does nothing.
// Scheduling of the mutator thread during the isolate start can cause this
// thread to safepoint.
// We want to avoid holding the message handler monitor during the safepoint
// operation to avoid possible deadlocks, which can occur if other threads are
// sending messages to this message handler.
//
// If isolate() returns nullptr [StartIsolateScope] does nothing.
ml->Exit();
StartIsolateScope start_isolate(isolate());
ml->Enter();
MessageStatus max_status = kOK;
Message::Priority min_priority =
@ -274,7 +287,7 @@ MessageHandler::MessageStatus MessageHandler::HandleNextMessage() {
MessageHandler::MessageStatus MessageHandler::PauseAndHandleAllMessages(
int64_t timeout_millis) {
MonitorLocker ml(&monitor_);
MonitorLocker ml(&monitor_, /*no_safepoint_scope=*/false);
ASSERT(task_running_);
ASSERT(!delete_me_);
#if defined(DEBUG)
@ -282,7 +295,13 @@ MessageHandler::MessageStatus MessageHandler::PauseAndHandleAllMessages(
#endif
paused_for_messages_ = true;
while (queue_->IsEmpty() && oob_queue_->IsEmpty()) {
Monitor::WaitResult wr = ml.Wait(timeout_millis);
Monitor::WaitResult wr;
{
// Ensure this thread is at a safepoint while we wait for new messages to
// arrive.
TransitionVMToNative transition(Thread::Current());
wr = ml.Wait(timeout_millis);
}
ASSERT(task_running_);
ASSERT(!delete_me_);
if (wr == Monitor::kTimedOut) {
@ -361,6 +380,12 @@ void MessageHandler::TaskCallback() {
// all pending OOB messages, or we may miss a request for vm
// shutdown.
MonitorLocker ml(&monitor_);
// This method is running on the message handler task. Which means no
// other message handler tasks will be started until this one sets
// [task_running_] to false.
ASSERT(task_running_);
#if !defined(PRODUCT)
if (ShouldPauseOnStart(kOK)) {
if (!is_paused_on_start()) {

View file

@ -315,7 +315,7 @@ class OSThread : public BaseThread {
static thread_local ThreadState* current_vm_thread_;
#endif
friend class Isolate; // to access set_thread(Thread*).
friend class IsolateGroup; // to access set_thread(Thread*).
friend class OSThreadIterator;
friend class ThreadInterrupterWin;
friend class ThreadInterrupterFuchsia;

View file

@ -44,6 +44,7 @@ class HierarchyInfo;
class Instance;
class Interpreter;
class Isolate;
class IsolateGroup;
class Library;
class Object;
class OSThread;
@ -358,9 +359,13 @@ class Thread : public ThreadState {
void EnterApiScope();
void ExitApiScope();
// The isolate that this thread is operating on, or NULL if none.
// The isolate that this thread is operating on, or nullptr if none.
Isolate* isolate() const { return isolate_; }
static intptr_t isolate_offset() { return OFFSET_OF(Thread, isolate_); }
// The isolate group that this thread is operating on, or nullptr if none.
IsolateGroup* isolate_group() const { return isolate_group_; }
bool IsMutatorThread() const;
bool CanCollectGarbage() const;
@ -883,6 +888,7 @@ class Thread : public ThreadState {
TaskKind task_kind_;
TimelineStream* dart_stream_;
IsolateGroup* isolate_group_ = nullptr;
mutable Monitor thread_lock_;
ApiLocalScope* api_reusable_scope_;
ApiLocalScope* api_top_scope_;
@ -968,6 +974,7 @@ class Thread : public ThreadState {
friend class Interpreter;
friend class InterruptChecker;
friend class Isolate;
friend class IsolateGroup;
friend class IsolateTestHelper;
friend class NoOOBMessageScope;
friend class Simulator;

View file

@ -41,50 +41,59 @@ void ThreadRegistry::ReturnThreadLocked(Thread* thread) {
ReturnToFreelistLocked(thread);
}
void ThreadRegistry::VisitObjectPointers(ObjectPointerVisitor* visitor,
void ThreadRegistry::VisitObjectPointers(Isolate* isolate_of_interest,
ObjectPointerVisitor* visitor,
ValidationPolicy validate_frames) {
MonitorLocker ml(threads_lock());
Thread* thread = active_list_;
while (thread != NULL) {
// The mutator thread is visited by the isolate itself (see
// [Isolate::VisitStackPointers]).
if (!thread->IsMutatorThread()) {
thread->VisitObjectPointers(visitor, validate_frames);
if (thread->isolate() == isolate_of_interest) {
// The mutator thread is visited by the isolate itself (see
// [Isolate::VisitStackPointers]).
if (!thread->IsMutatorThread()) {
thread->VisitObjectPointers(visitor, validate_frames);
}
}
thread = thread->next_;
}
}
void ThreadRegistry::ReleaseStoreBuffers() {
void ThreadRegistry::ReleaseStoreBuffers(Isolate* isolate_of_interest) {
MonitorLocker ml(threads_lock());
Thread* thread = active_list_;
while (thread != NULL) {
if (!thread->BypassSafepoints()) {
thread->ReleaseStoreBuffer();
if (thread->isolate() == isolate_of_interest) {
if (!thread->BypassSafepoints()) {
thread->ReleaseStoreBuffer();
}
}
thread = thread->next_;
}
}
void ThreadRegistry::AcquireMarkingStacks() {
void ThreadRegistry::AcquireMarkingStacks(Isolate* isolate_of_interest) {
MonitorLocker ml(threads_lock());
Thread* thread = active_list_;
while (thread != NULL) {
if (!thread->BypassSafepoints()) {
thread->MarkingStackAcquire();
thread->DeferredMarkingStackAcquire();
if (thread->isolate() == isolate_of_interest) {
if (!thread->BypassSafepoints()) {
thread->MarkingStackAcquire();
thread->DeferredMarkingStackAcquire();
}
}
thread = thread->next_;
}
}
void ThreadRegistry::ReleaseMarkingStacks() {
void ThreadRegistry::ReleaseMarkingStacks(Isolate* isolate_of_interest) {
MonitorLocker ml(threads_lock());
Thread* thread = active_list_;
while (thread != NULL) {
if (!thread->BypassSafepoints()) {
thread->MarkingStackRelease();
thread->DeferredMarkingStackRelease();
if (thread->isolate() == isolate_of_interest) {
if (!thread->BypassSafepoints()) {
thread->MarkingStackRelease();
thread->DeferredMarkingStackRelease();
}
}
thread = thread->next_;
}
@ -102,23 +111,28 @@ void ThreadRegistry::PrintJSON(JSONStream* stream) const {
}
#endif
intptr_t ThreadRegistry::CountZoneHandles() const {
intptr_t ThreadRegistry::CountZoneHandles(Isolate* isolate_of_interest) const {
MonitorLocker ml(threads_lock());
intptr_t count = 0;
Thread* current = active_list_;
while (current != NULL) {
count += current->CountZoneHandles();
if (current->isolate() == isolate_of_interest) {
count += current->CountZoneHandles();
}
current = current->next_;
}
return count;
}
intptr_t ThreadRegistry::CountScopedHandles() const {
intptr_t ThreadRegistry::CountScopedHandles(
Isolate* isolate_of_interest) const {
MonitorLocker ml(threads_lock());
intptr_t count = 0;
Thread* current = active_list_;
while (current != NULL) {
count += current->CountScopedHandles();
if (current->isolate() == isolate_of_interest) {
count += current->CountScopedHandles();
}
current = current->next_;
}
return count;

View file

@ -25,22 +25,20 @@ class ThreadRegistry {
ThreadRegistry() : threads_lock_(), active_list_(NULL), free_list_(NULL) {}
~ThreadRegistry();
void VisitObjectPointers(ObjectPointerVisitor* visitor,
void VisitObjectPointers(Isolate* isolate_of_interest,
ObjectPointerVisitor* visitor,
ValidationPolicy validate_frames);
void ReleaseStoreBuffers();
void AcquireMarkingStacks();
void ReleaseMarkingStacks();
void ReleaseStoreBuffers(Isolate* isolate_of_interest);
void AcquireMarkingStacks(Isolate* isolate_of_interest);
void ReleaseMarkingStacks(Isolate* isolate_of_interest);
#ifndef PRODUCT
void PrintJSON(JSONStream* stream) const;
#endif
// Calculates the sum of the max memory usage in bytes of each thread.
uintptr_t ThreadHighWatermarksTotalLocked() const;
intptr_t CountZoneHandles() const;
intptr_t CountScopedHandles() const;
intptr_t CountZoneHandles(Isolate* isolate_of_interest) const;
intptr_t CountScopedHandles(Isolate* isolate_of_interest) const;
private:
Thread* active_list() const { return active_list_; }
@ -60,6 +58,7 @@ class ThreadRegistry {
Thread* free_list_; // Free list of Thread objects that can be reused.
friend class Isolate;
friend class IsolateGroup;
friend class SafepointHandler;
friend class Scavenger;
DISALLOW_COPY_AND_ASSIGN(ThreadRegistry);