| // 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 |