b40775549f
- Ensure that no new OSThreads are created once the VM is in shutdown mode - Do not query thread_list_head_ without a lock (this leads to the race which causes two threads to delete the lock and TLS at the same time) BUG=25236 R=zra@google.com Review URL: https://codereview.chromium.org/1537543002 .
256 lines
7.5 KiB
C++
256 lines
7.5 KiB
C++
// Copyright (c) 2015, 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/thread_registry.h"
|
|
|
|
#include "vm/isolate.h"
|
|
#include "vm/lockers.h"
|
|
|
|
namespace dart {
|
|
|
|
ThreadRegistry::~ThreadRegistry() {
|
|
// Go over the free thread list and delete the thread objects.
|
|
{
|
|
MonitorLocker ml(monitor_);
|
|
// At this point the active list should be empty.
|
|
ASSERT(active_list_ == NULL);
|
|
// We have cached the mutator thread, delete it.
|
|
delete mutator_thread_;
|
|
mutator_thread_ = NULL;
|
|
// Now delete all the threads in the free list.
|
|
while (free_list_ != NULL) {
|
|
Thread* thread = free_list_;
|
|
free_list_ = thread->next_;
|
|
delete thread;
|
|
}
|
|
}
|
|
|
|
// Delete monitor.
|
|
delete monitor_;
|
|
}
|
|
|
|
|
|
void ThreadRegistry::SafepointThreads() {
|
|
MonitorLocker ml(monitor_);
|
|
// First wait for any older rounds that are still in progress.
|
|
while (in_rendezvous_) {
|
|
// Assert we are not the organizer trying to nest calls to SafepointThreads.
|
|
ASSERT(remaining_ > 0);
|
|
CheckSafepointLocked();
|
|
}
|
|
// Start a new round.
|
|
in_rendezvous_ = true;
|
|
++round_; // Overflows after 240+ years @ 10^9 safepoints per second.
|
|
remaining_ = CountScheduledLocked();
|
|
Isolate* isolate = Isolate::Current();
|
|
// We only expect this method to be called from within the isolate itself.
|
|
ASSERT(isolate->thread_registry() == this);
|
|
--remaining_; // Exclude this thread from the count.
|
|
// Ensure the main mutator will reach a safepoint (could be running Dart).
|
|
if (!Thread::Current()->IsMutatorThread()) {
|
|
isolate->ScheduleInterrupts(Isolate::kVMInterrupt);
|
|
}
|
|
while (remaining_ > 0) {
|
|
ml.Wait(Monitor::kNoTimeout);
|
|
}
|
|
}
|
|
|
|
|
|
void ThreadRegistry::ResumeAllThreads() {
|
|
MonitorLocker ml(monitor_);
|
|
ASSERT(in_rendezvous_);
|
|
in_rendezvous_ = false;
|
|
ml.NotifyAll();
|
|
}
|
|
|
|
|
|
Thread* ThreadRegistry::Schedule(Isolate* isolate,
|
|
bool is_mutator,
|
|
bool bypass_safepoint) {
|
|
MonitorLocker ml(monitor_);
|
|
// Wait for any rendezvous in progress.
|
|
while (!bypass_safepoint && in_rendezvous_) {
|
|
ml.Wait(Monitor::kNoTimeout);
|
|
}
|
|
Thread* thread = NULL;
|
|
OSThread* os_thread = OSThread::Current();
|
|
if (os_thread != NULL) {
|
|
ASSERT(isolate->heap() != NULL);
|
|
// First get a Thread structure. (we special case the mutator thread
|
|
// by reusing the cached structure, see comment in 'thread_registry.h').
|
|
if (is_mutator) {
|
|
if (mutator_thread_ == NULL) {
|
|
mutator_thread_ = GetThreadFromFreelist(isolate);
|
|
}
|
|
thread = mutator_thread_;
|
|
} else {
|
|
thread = GetThreadFromFreelist(isolate);
|
|
ASSERT(thread->api_top_scope() == NULL);
|
|
}
|
|
// Now add this Thread to the active list for the isolate.
|
|
AddThreadToActiveList(thread);
|
|
// Set up other values and set the TLS value.
|
|
thread->isolate_ = isolate;
|
|
thread->heap_ = isolate->heap();
|
|
thread->set_os_thread(os_thread);
|
|
os_thread->set_thread(thread);
|
|
Thread::SetCurrent(thread);
|
|
os_thread->EnableThreadInterrupts();
|
|
}
|
|
return thread;
|
|
}
|
|
|
|
|
|
void ThreadRegistry::Unschedule(Thread* thread,
|
|
bool is_mutator,
|
|
bool bypass_safepoint) {
|
|
MonitorLocker ml(monitor_);
|
|
OSThread* os_thread = thread->os_thread();
|
|
ASSERT(os_thread != NULL);
|
|
os_thread->DisableThreadInterrupts();
|
|
os_thread->set_thread(NULL);
|
|
OSThread::SetCurrent(os_thread);
|
|
thread->isolate_ = NULL;
|
|
thread->heap_ = NULL;
|
|
thread->set_os_thread(NULL);
|
|
// Remove thread from the active list for the isolate.
|
|
RemoveThreadFromActiveList(thread);
|
|
// Return thread to the free list (we special case the mutator
|
|
// thread by holding on to it, see comment in 'thread_registry.h').
|
|
if (!is_mutator) {
|
|
ASSERT(thread->api_top_scope() == NULL);
|
|
ReturnThreadToFreelist(thread);
|
|
}
|
|
if (!bypass_safepoint && in_rendezvous_) {
|
|
// Don't wait for this thread.
|
|
ASSERT(remaining_ > 0);
|
|
if (--remaining_ == 0) {
|
|
ml.NotifyAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ThreadRegistry::VisitObjectPointers(ObjectPointerVisitor* visitor,
|
|
bool validate_frames) {
|
|
MonitorLocker ml(monitor_);
|
|
Thread* thread = active_list_;
|
|
while (thread != NULL) {
|
|
if (thread->zone() != NULL) {
|
|
thread->zone()->VisitObjectPointers(visitor);
|
|
}
|
|
thread->VisitObjectPointers(visitor);
|
|
// Iterate over all the stack frames and visit objects on the stack.
|
|
StackFrameIterator frames_iterator(thread->top_exit_frame_info(),
|
|
validate_frames);
|
|
StackFrame* frame = frames_iterator.NextFrame();
|
|
while (frame != NULL) {
|
|
frame->VisitObjectPointers(visitor);
|
|
frame = frames_iterator.NextFrame();
|
|
}
|
|
thread = thread->next_;
|
|
}
|
|
}
|
|
|
|
|
|
void ThreadRegistry::PrepareForGC() {
|
|
MonitorLocker ml(monitor_);
|
|
Thread* thread = active_list_;
|
|
while (thread != NULL) {
|
|
thread->PrepareForGC();
|
|
thread = thread->next_;
|
|
}
|
|
}
|
|
|
|
|
|
void ThreadRegistry::AddThreadToActiveList(Thread* thread) {
|
|
ASSERT(thread != NULL);
|
|
ASSERT(monitor_->IsOwnedByCurrentThread());
|
|
thread->next_ = active_list_;
|
|
active_list_ = thread;
|
|
}
|
|
|
|
|
|
void ThreadRegistry::RemoveThreadFromActiveList(Thread* thread) {
|
|
ASSERT(thread != NULL);
|
|
ASSERT(monitor_->IsOwnedByCurrentThread());
|
|
Thread* prev = NULL;
|
|
Thread* current = active_list_;
|
|
while (current != NULL) {
|
|
if (current == thread) {
|
|
if (prev == NULL) {
|
|
active_list_ = current->next_;
|
|
} else {
|
|
prev->next_ = current->next_;
|
|
}
|
|
break;
|
|
}
|
|
prev = current;
|
|
current = current->next_;
|
|
}
|
|
}
|
|
|
|
|
|
Thread* ThreadRegistry::GetThreadFromFreelist(Isolate* isolate) {
|
|
ASSERT(monitor_->IsOwnedByCurrentThread());
|
|
Thread* thread = NULL;
|
|
// Get thread structure from free list or create a new one.
|
|
if (free_list_ == NULL) {
|
|
thread = new Thread(isolate);
|
|
} else {
|
|
thread = free_list_;
|
|
free_list_ = thread->next_;
|
|
}
|
|
return thread;
|
|
}
|
|
|
|
void ThreadRegistry::ReturnThreadToFreelist(Thread* thread) {
|
|
ASSERT(thread != NULL);
|
|
ASSERT(thread->os_thread_ == NULL);
|
|
ASSERT(thread->isolate_ == NULL);
|
|
ASSERT(thread->heap_ == NULL);
|
|
ASSERT(monitor_->IsOwnedByCurrentThread());
|
|
// Add thread to the free list.
|
|
thread->next_ = free_list_;
|
|
free_list_ = thread;
|
|
}
|
|
|
|
|
|
void ThreadRegistry::CheckSafepointLocked() {
|
|
int64_t last_round = -1;
|
|
while (in_rendezvous_) {
|
|
ASSERT(round_ >= last_round);
|
|
if (round_ != last_round) {
|
|
ASSERT((last_round == -1) || (round_ == (last_round + 1)));
|
|
last_round = round_;
|
|
// Participate in this round.
|
|
if (--remaining_ == 0) {
|
|
// Ensure the organizing thread is notified.
|
|
// TODO(koda): Use separate condition variables and plain 'Notify'.
|
|
monitor_->NotifyAll();
|
|
}
|
|
}
|
|
monitor_->Wait(Monitor::kNoTimeout);
|
|
// Note: Here, round_ is needed to detect and distinguish two cases:
|
|
// a) The old rendezvous is still in progress, so just keep waiting, or
|
|
// b) after ResumeAllThreads, another call to SafepointThreads was
|
|
// made before this thread got a chance to reaquire monitor_, thus this
|
|
// thread should (again) decrease remaining_ to indicate cooperation in
|
|
// this new round.
|
|
}
|
|
}
|
|
|
|
|
|
intptr_t ThreadRegistry::CountScheduledLocked() {
|
|
intptr_t count = 0;
|
|
Thread* current = active_list_;
|
|
while (current != NULL) {
|
|
++count;
|
|
current = current->next_;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
} // namespace dart
|