9e19d236ca
fields in a thread (i.e fields that are not Dart VM related) - Split the Thread structure to be a pure Dart per thread structure and add a pointer to os_thread which points to the OSThread structure - Change Schedule/UnSchedule to set the Dart Thread structure as the TLS of the thread when it is inside the Dart world and reset the TLS back to the OSThread strcuture when is exits the Dart World. - Moved the stack_base and few stack size related functions to OSThread from Isolate R=johnmccutchan@google.com, zra@google.com Review URL: https://codereview.chromium.org/1439483003 .
228 lines
6.6 KiB
C++
228 lines
6.6 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 mutator thread should be the only thread
|
|
// in the active list, delete it.
|
|
ASSERT(active_list_->next_ == NULL);
|
|
ASSERT(active_list_ == mutator_thread_);
|
|
delete mutator_thread_;
|
|
active_list_ = NULL;
|
|
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);
|
|
// TODO(koda): Rename Thread::PrepareForGC and call it here?
|
|
--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();
|
|
ASSERT(os_thread != NULL);
|
|
ASSERT(isolate->heap() != NULL);
|
|
if (is_mutator) {
|
|
if (mutator_thread_ == NULL) {
|
|
mutator_thread_ = GetThreadFromFreelist(isolate);
|
|
}
|
|
thread = mutator_thread_;
|
|
} else {
|
|
thread = GetThreadFromFreelist(isolate);
|
|
}
|
|
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();
|
|
thread->isolate_ = NULL;
|
|
thread->heap_ = NULL;
|
|
thread->set_os_thread(NULL);
|
|
os_thread->set_thread(NULL);
|
|
OSThread::SetCurrent(os_thread);
|
|
if (!is_mutator) {
|
|
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_;
|
|
}
|
|
}
|
|
|
|
|
|
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_;
|
|
}
|
|
// Add thread to active list.
|
|
thread->next_ = active_list_;
|
|
active_list_ = thread;
|
|
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());
|
|
// First remove the thread from the active list.
|
|
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_;
|
|
}
|
|
// Now 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.
|
|
// TODO(koda): Rename Thread::PrepareForGC and call it here?
|
|
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
|