mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[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:
parent
9ec6a48613
commit
0a2993687b
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue