blob: 44c0d133460cd1aa7afda44e1d453d0a9759debe [file] [log] [blame]
// 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() {
ReclaimTimelineBlocks();
// 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 (!isolate->MutatorThreadIsCurrentThread()) {
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();
}
void ThreadRegistry::PruneThread(Thread* thread) {
MonitorLocker ml(monitor_);
intptr_t length = entries_.length();
if (length == 0) {
return;
}
intptr_t found_index = -1;
for (intptr_t index = 0; index < length; index++) {
if (entries_.At(index).thread == thread) {
found_index = index;
break;
}
}
if (found_index < 0) {
return;
}
{
TimelineEventRecorder* recorder = Timeline::recorder();
if (recorder != NULL) {
// Cleanup entry.
Entry& entry_to_remove = entries_[found_index];
ReclaimTimelineBlockLocked(&entry_to_remove);
}
}
if (found_index != (length - 1)) {
// Swap with last entry.
entries_.Swap(found_index, length - 1);
}
entries_.RemoveLast();
}
void ThreadRegistry::ReclaimTimelineBlocks() {
// Each thread that is scheduled in this isolate may have a cached timeline
// block. Mark these timeline blocks as finished.
MonitorLocker ml(monitor_);
TimelineEventRecorder* recorder = Timeline::recorder();
if (recorder != NULL) {
for (intptr_t i = 0; i < entries_.length(); i++) {
// NOTE: It is only safe to access |entry.state| here.
Entry& entry = entries_[i];
ReclaimTimelineBlockLocked(&entry);
}
}
}
void ThreadRegistry::ReclaimTimelineBlockLocked(Entry* entry) {
if (entry == NULL) {
return;
}
TimelineEventRecorder* recorder = Timeline::recorder();
if (!entry->scheduled && (entry->state.timeline_block != NULL)) {
// Currently unscheduled thread.
recorder->FinishBlock(entry->state.timeline_block);
entry->state.timeline_block = NULL;
} else if (entry->scheduled) {
// Currently scheduled thread.
Thread* thread = entry->thread;
// Take |Thread| lock.
MutexLocker thread_lock(thread->timeline_block_lock());
recorder->FinishBlock(thread->timeline_block());
thread->set_timeline_block(NULL);
}
}
ThreadRegistry::EntryIterator::EntryIterator(ThreadRegistry* registry)
: index_(0),
registry_(NULL) {
Reset(registry);
}
ThreadRegistry::EntryIterator::~EntryIterator() {
Reset(NULL);
}
void ThreadRegistry::EntryIterator::Reset(ThreadRegistry* registry) {
// Reset index.
index_ = 0;
// Unlock old registry.
if (registry_ != NULL) {
registry_->monitor_->Exit();
}
registry_ = registry;
// Lock new registry.
if (registry_ != NULL) {
registry_->monitor_->Enter();
}
}
bool ThreadRegistry::EntryIterator::HasNext() const {
if (registry_ == NULL) {
return false;
}
return index_ < registry_->entries_.length();
}
const ThreadRegistry::Entry& ThreadRegistry::EntryIterator::Next() {
ASSERT(HasNext());
return registry_->entries_.At(index_++);
}
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;
for (int i = 0; i < entries_.length(); ++i) {
const Entry& entry = entries_[i];
if (entry.scheduled) {
++count;
}
}
return count;
}
} // namespace dart