blob: 90d83f0b67876ca1739fbb5d66e0ecab0e08aadb [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/globals.h"
#include "vm/growable_array.h"
#include "vm/isolate.h"
#include "vm/lockers.h"
#include "vm/stack_frame.h"
#include "vm/thread.h"
namespace dart {
// Unordered collection of threads relating to a particular isolate.
class ThreadRegistry {
: monitor_(new Monitor()),
round_(0) {}
// Bring all threads in this isolate to a safepoint. The caller is
// expected to be implicitly at a safepoint. The threads will wait
// until ResumeAllThreads is called. First participates in any
// already pending rendezvous requested by another thread. Any
// thread that tries to enter this isolate during rendezvous will
// wait in RestoreStateTo. Nesting is not supported: the caller must
// call ResumeAllThreads before making further calls to
// SafepointThreads.
void SafepointThreads();
// Unblocks all threads participating in the rendezvous that was organized
// by a prior call to SafepointThreads.
// TODO(koda): Consider adding a scope helper to avoid omitting this call.
void ResumeAllThreads();
// Indicate that the current thread is at a safepoint, and offer to wait for
// any pending rendezvous request (if none, returns immediately).
void CheckSafepoint() {
MonitorLocker ml(monitor_);
bool RestoreStateTo(Thread* thread, Thread::State* state,
bool bypass_safepoint) {
MonitorLocker ml(monitor_);
// Wait for any rendezvous in progress.
while (!bypass_safepoint && in_rendezvous_) {
Entry* entry = FindEntry(thread);
if (entry != NULL) {
Thread::State st = entry->state;
// TODO(koda): Support same thread re-entering same isolate with
// Dart frames in between. For now, just assert it doesn't happen.
if (st.top_exit_frame_info != thread->top_exit_frame_info()) {
ASSERT(thread->top_exit_frame_info() == 0 ||
thread->top_exit_frame_info() > st.top_exit_frame_info);
entry->scheduled = true;
#if defined(DEBUG)
// State field is not in use, so zap it.
memset(&entry->state, 0xda, sizeof(entry->state));
*state = st;
return true;
Entry new_entry;
new_entry.thread = thread;
new_entry.scheduled = true;
#if defined(DEBUG)
// State field is not in use, so zap it.
memset(&new_entry.state, 0xda, sizeof(new_entry.state));
return false;
void SaveStateFrom(Thread* thread, const Thread::State& state,
bool bypass_safepoint) {
MonitorLocker ml(monitor_);
Entry* entry = FindEntry(thread);
ASSERT(entry != NULL);
entry->scheduled = false;
entry->state = state;
if (!bypass_safepoint && in_rendezvous_) {
// Don't wait for this thread.
ASSERT(remaining_ > 0);
if (--remaining_ == 0) {
bool Contains(Thread* thread) {
MonitorLocker ml(monitor_);
return (FindEntry(thread) != NULL);
void CheckNotScheduled(Isolate* isolate) {
MonitorLocker ml(monitor_);
for (int i = 0; i < entries_.length(); ++i) {
const Entry& entry = entries_[i];
if (entry.scheduled) {
FATAL3("Isolate %p still scheduled on %p (whose isolate_ is %p)\n",
void VisitObjectPointers(ObjectPointerVisitor* visitor,
bool validate_frames) {
MonitorLocker ml(monitor_);
for (int i = 0; i < entries_.length(); ++i) {
const Entry& entry = entries_[i];
const Thread::State& state =
entry.scheduled ? entry.thread->state_ : entry.state;
if ( != NULL) {>VisitObjectPointers(visitor);
if (entry.scheduled) {
ASSERT(entry.thread != NULL);
// Iterate over all the stack frames and visit objects on the stack.
StackFrameIterator frames_iterator(state.top_exit_frame_info,
StackFrame* frame = frames_iterator.NextFrame();
while (frame != NULL) {
frame = frames_iterator.NextFrame();
void PruneThread(Thread* thread);
void ReclaimTimelineBlocks();
struct Entry {
// NOTE: |thread| is deleted automatically when the thread exits.
// In other words, it is not safe to dereference |thread| unless you are on
// the thread itself.
Thread* thread;
bool scheduled;
Thread::State state;
class EntryIterator {
explicit EntryIterator(ThreadRegistry* registry);
// Returns false when there are no more entries.
bool HasNext() const;
// Returns the next entry and moves forward.
const Entry& Next();
void Reset(ThreadRegistry* registry);
intptr_t index_;
ThreadRegistry* registry_;
// Returns Entry corresponding to thread in registry or NULL.
// Note: Lock should be taken before this function is called.
// TODO(koda): Add method Monitor::IsOwnedByCurrentThread.
Entry* FindEntry(Thread* thread) {
for (int i = 0; i < entries_.length(); ++i) {
if (entries_[i].thread == thread) {
return &entries_[i];
return NULL;
// NOTE: Lock should be taken before this function is called.
void ReclaimTimelineBlockLocked(Entry* entry);
// Note: Lock should be taken before this function is called.
void CheckSafepointLocked();
// Returns the number threads that are scheduled on this isolate.
// Note: Lock should be taken before this function is called.
intptr_t CountScheduledLocked();
Monitor* monitor_; // All access is synchronized through this monitor.
MallocGrowableArray<Entry> entries_;
// Safepoint rendezvous state.
bool in_rendezvous_; // A safepoint rendezvous request is in progress.
intptr_t remaining_; // Number of threads yet to reach their safepoint.
int64_t round_; // Counter, to prevent missing updates to remaining_
// (see comments in CheckSafepointLocked).
} // namespace dart