Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(247)

Unified Diff: runtime/vm/thread_interrupter.cc

Issue 109803002: Profiler Take 2 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/thread_interrupter.h ('k') | runtime/vm/thread_interrupter_android.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/thread_interrupter.cc
diff --git a/runtime/vm/thread_interrupter.cc b/runtime/vm/thread_interrupter.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7085a4397d3748d1e1b58c80ca9fdb3cf3186284
--- /dev/null
+++ b/runtime/vm/thread_interrupter.cc
@@ -0,0 +1,387 @@
+// Copyright (c) 2013, 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/simulator.h"
+#include "vm/thread_interrupter.h"
+
+namespace dart {
+
+// Notes:
+//
+// The ThreadInterrupter interrupts all registered threads once per
+// interrupt period (default is every millisecond). While the thread is
+// interrupted, the thread's interrupt callback is invoked. Callbacks cannot
+// rely on being executed on the interrupted thread.
+//
+// There are two mechanisms used to interrupt a thread. The first, used on OSs
+// with pthreads (Android, Linux, and Mac), is thread specific signal delivery.
+// The second, used on Windows, is explicit suspend and resume thread system
+// calls. Signal delivery forbids taking locks and allocating memory (which
+// takes a lock). Explicit suspend and resume means that the interrupt callback
+// will not be executing on the interrupted thread, making it meaningless to
+// access TLS from within the thread interrupt callback. Combining these
+// limitations, thread interrupt callbacks are forbidden from:
+//
+// * Accessing TLS.
+// * Allocating memory.
+// * Taking a lock.
+//
+// The ThreadInterrupter has a single monitor (monitor_). This monitor guards
+// access to the list of threads registered to receive interrupts (threads_).
+//
+// A thread can only register and unregister itself. Each thread has a heap
+// allocated ThreadState. A thread's ThreadState is lazily allocated the first
+// time the thread is registered. A pointer to a thread's ThreadState is stored
+// in the list of threads registered to receive interrupts (threads_) and in
+// thread local storage. When a thread's ThreadState is being modified, the
+// thread local storage pointer is temporarily set to NULL while the
+// modification is occurring. After the ThreadState has been updated, the
+// thread local storage pointer is set again. This has an important side
+// effect: if the thread is interrupted by a signal handler during a ThreadState
+// update the signal handler will immediately return.
+
+DEFINE_FLAG(bool, trace_thread_interrupter, false,
+ "Trace thread interrupter");
+
+bool ThreadInterrupter::initialized_ = false;
+bool ThreadInterrupter::shutdown_ = false;
+bool ThreadInterrupter::thread_running_ = false;
+ThreadId ThreadInterrupter::interrupter_thread_id_ = Thread::kInvalidThreadId;
+Monitor* ThreadInterrupter::monitor_ = NULL;
+intptr_t ThreadInterrupter::interrupt_period_ = 1000;
+ThreadLocalKey ThreadInterrupter::thread_state_key_ =
+ Thread::kUnsetThreadLocalKey;
+ThreadInterrupter::ThreadState** ThreadInterrupter::threads_ = NULL;
+intptr_t ThreadInterrupter::threads_capacity_ = 0;
+intptr_t ThreadInterrupter::threads_size_ = 0;
+
+
+void ThreadInterrupter::InitOnce() {
+ ASSERT(!initialized_);
+ initialized_ = true;
+ ASSERT(thread_state_key_ == Thread::kUnsetThreadLocalKey);
+ thread_state_key_ = Thread::CreateThreadLocal();
+ ASSERT(thread_state_key_ != Thread::kUnsetThreadLocalKey);
+ monitor_ = new Monitor();
+ ResizeThreads(16);
+ if (FLAG_trace_thread_interrupter) {
+ OS::Print("ThreadInterrupter starting up.\n");
+ }
+ ASSERT(interrupter_thread_id_ == Thread::kInvalidThreadId);
+ {
+ MonitorLocker startup_ml(monitor_);
+ Thread::Start(ThreadMain, 0);
+ while (!thread_running_) {
+ startup_ml.Wait();
+ }
+ }
+ ASSERT(interrupter_thread_id_ != Thread::kInvalidThreadId);
+ if (FLAG_trace_thread_interrupter) {
+ OS::Print("ThreadInterrupter running.\n");
+ }
+}
+
+
+void ThreadInterrupter::Shutdown() {
+ if (shutdown_) {
+ // Already shutdown.
+ return;
+ }
+ ASSERT(initialized_);
+ if (FLAG_trace_thread_interrupter) {
+ OS::Print("ThreadInterrupter shutting down.\n");
+ }
+ intptr_t size_at_shutdown = 0;
+ {
+ MonitorLocker ml(monitor_);
+ shutdown_ = true;
+ size_at_shutdown = threads_size_;
+ threads_size_ = 0;
+ threads_capacity_ = 0;
+ free(threads_);
+ threads_ = NULL;
+ }
+ {
+ MonitorLocker shutdown_ml(monitor_);
+ while (thread_running_) {
+ shutdown_ml.Wait();
+ }
+ }
+ interrupter_thread_id_ = Thread::kInvalidThreadId;
+ if (FLAG_trace_thread_interrupter) {
+ OS::Print("ThreadInterrupter shut down (%" Pd ").\n", size_at_shutdown);
+ }
+}
+
+// Delay between interrupts.
+void ThreadInterrupter::SetInterruptPeriod(intptr_t period) {
+ if (shutdown_) {
+ return;
+ }
+ ASSERT(initialized_);
+ ASSERT(period > 0);
+ {
+ MonitorLocker ml(monitor_);
+ interrupt_period_ = period;
+ }
+}
+
+
+// Register the currently running thread for interrupts. If the current thread
+// is already registered, callback and data will be updated.
+void ThreadInterrupter::Register(ThreadInterruptCallback callback, void* data) {
+ if (shutdown_) {
+ return;
+ }
+ ASSERT(initialized_);
+ {
+ MonitorLocker ml(monitor_);
+ _EnsureThreadStateCreated();
+ // Set callback and data.
+ UpdateStateObject(callback, data);
+ _Enable();
+ }
+}
+
+
+// Unregister the currently running thread for interrupts.
+void ThreadInterrupter::Unregister() {
+ if (shutdown_) {
+ return;
+ }
+ ASSERT(initialized_);
+ {
+ MonitorLocker ml(monitor_);
+ _EnsureThreadStateCreated();
+ // Clear callback and data.
+ UpdateStateObject(NULL, NULL);
+ _Disable();
+ }
+}
+
+
+void ThreadInterrupter::Enable() {
+ if (shutdown_) {
+ return;
+ }
+ ASSERT(initialized_);
+ {
+ MonitorLocker ml(monitor_);
+ _EnsureThreadStateCreated();
+ _Enable();
+ }
+}
+
+
+void ThreadInterrupter::Disable() {
+ if (shutdown_) {
+ return;
+ }
+ ASSERT(initialized_);
+ {
+ MonitorLocker ml(monitor_);
+ _EnsureThreadStateCreated();
+ _Disable();
+ }
+}
+
+
+void ThreadInterrupter::_EnsureThreadStateCreated() {
+ ThreadState* state = CurrentThreadState();
+ if (state == NULL) {
+ // Create thread state object lazily.
+ ThreadId current_thread = Thread::GetCurrentThreadId();
+ if (FLAG_trace_thread_interrupter) {
+ intptr_t tid = Thread::ThreadIdToIntPtr(current_thread);
+ OS::Print("ThreadInterrupter Tracking %p\n",
+ reinterpret_cast<void*>(tid));
+ }
+ state = new ThreadState();
+ state->callback = NULL;
+ state->data = NULL;
+ state->id = current_thread;
+ SetCurrentThreadState(state);
+ }
+}
+
+
+void ThreadInterrupter::_Enable() {
+ // Must be called with monitor_ locked.
+ ThreadId current_thread = Thread::GetCurrentThreadId();
+ if (Thread::Compare(current_thread, interrupter_thread_id_)) {
+ return;
+ }
+ intptr_t i = FindThreadIndex(current_thread);
+ if (i >= 0) {
+ return;
+ }
+ AddThread(current_thread);
+ if (FLAG_trace_thread_interrupter) {
+ intptr_t tid = Thread::ThreadIdToIntPtr(current_thread);
+ OS::Print("ThreadInterrupter Added %p\n", reinterpret_cast<void*>(tid));
+ }
+}
+
+void ThreadInterrupter::_Disable() {
+ // Must be called with monitor_ locked.
+ ThreadId current_thread = Thread::GetCurrentThreadId();
+ if (Thread::Compare(current_thread, interrupter_thread_id_)) {
+ return;
+ }
+ intptr_t index = FindThreadIndex(current_thread);
+ if (index < 0) {
+ // Not registered.
+ return;
+ }
+ ThreadState* state = RemoveThread(index);
+ ASSERT(state != NULL);
+ ASSERT(state == ThreadInterrupter::CurrentThreadState());
+ if (FLAG_trace_thread_interrupter) {
+ intptr_t tid = Thread::ThreadIdToIntPtr(current_thread);
+ OS::Print("ThreadInterrupter Removed %p\n", reinterpret_cast<void*>(tid));
+ }
+}
+
+void ThreadInterrupter::UpdateStateObject(ThreadInterruptCallback callback,
+ void* data) {
+ // Must be called with monitor_ locked.
+ ThreadState* state = CurrentThreadState();
+ ThreadId current_thread = Thread::GetCurrentThreadId();
+ ASSERT(state != NULL);
+ ASSERT(Thread::Compare(state->id, Thread::GetCurrentThreadId()));
+ SetCurrentThreadState(NULL);
+ // It is now safe to modify the state object. If an interrupt occurs,
+ // the current thread state will be NULL.
+ state->callback = callback;
+ state->data = data;
+ SetCurrentThreadState(state);
+ if (FLAG_trace_thread_interrupter) {
+ intptr_t tid = Thread::ThreadIdToIntPtr(current_thread);
+ if (callback == NULL) {
+ OS::Print("ThreadInterrupter Cleared %p\n", reinterpret_cast<void*>(tid));
+ } else {
+ OS::Print("ThreadInterrupter Updated %p\n", reinterpret_cast<void*>(tid));
+ }
+ }
+}
+
+
+ThreadInterrupter::ThreadState* ThreadInterrupter::CurrentThreadState() {
+ ThreadState* state = reinterpret_cast<ThreadState*>(
+ Thread::GetThreadLocal(thread_state_key_));
+ return state;
+}
+
+
+void ThreadInterrupter::SetCurrentThreadState(ThreadState* state) {
+ Thread::SetThreadLocal(thread_state_key_, reinterpret_cast<uword>(state));
+}
+
+
+void ThreadInterrupter::ResizeThreads(intptr_t new_capacity) {
+ // Must be called with monitor_ locked.
+ ASSERT(new_capacity < kMaxThreads);
+ ASSERT(new_capacity > threads_capacity_);
+ ThreadState* state = NULL;
+ threads_ = reinterpret_cast<ThreadState**>(
+ realloc(threads_, sizeof(state) * new_capacity));
+ for (intptr_t i = threads_capacity_; i < new_capacity; i++) {
+ threads_[i] = NULL;
+ }
+ threads_capacity_ = new_capacity;
+}
+
+
+void ThreadInterrupter::AddThread(ThreadId id) {
+ // Must be called with monitor_ locked.
+ if (threads_ == NULL) {
+ // We are shutting down.
+ return;
+ }
+ ThreadState* state = CurrentThreadState();
+ if (state->callback == NULL) {
+ // No callback.
+ return;
+ }
+ if (threads_size_ == threads_capacity_) {
+ ResizeThreads(threads_capacity_ == 0 ? 16 : threads_capacity_ * 2);
+ }
+ threads_[threads_size_] = state;
+ threads_size_++;
+}
+
+
+intptr_t ThreadInterrupter::FindThreadIndex(ThreadId id) {
+ // Must be called with monitor_ locked.
+ if (threads_ == NULL) {
+ // We are shutting down.
+ return -1;
+ }
+ for (intptr_t i = 0; i < threads_size_; i++) {
+ if (threads_[i]->id == id) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+ThreadInterrupter::ThreadState* ThreadInterrupter::RemoveThread(intptr_t i) {
+ // Must be called with monitor_ locked.
+ if (threads_ == NULL) {
+ // We are shutting down.
+ return NULL;
+ }
+ ASSERT(i < threads_size_);
+ ThreadState* state = threads_[i];
+ ASSERT(state != NULL);
+ intptr_t last = threads_size_ - 1;
+ if (i != last) {
+ threads_[i] = threads_[last];
+ }
+ // Mark last as NULL.
+ threads_[last] = NULL;
+ // Pop.
+ threads_size_--;
+ return state;
+}
+
+
+void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data) {
+ // NoOp.
+}
+
+void ThreadInterrupter::ThreadMain(uword parameters) {
+ ASSERT(initialized_);
+ InstallSignalHandler();
+ if (FLAG_trace_thread_interrupter) {
+ OS::Print("ThreadInterrupter thread running.\n");
+ }
+ {
+ // Signal to main thread we are ready.
+ MonitorLocker startup_ml(monitor_);
+ thread_running_ = true;
+ interrupter_thread_id_ = Thread::GetCurrentThreadId();
+ startup_ml.Notify();
+ }
+ {
+ MonitorLocker ml(monitor_);
+ while (!shutdown_) {
+ int64_t current_time = OS::GetCurrentTimeMicros();
+ InterruptThreads(current_time);
+ ml.WaitMicros(interrupt_period_);
+ }
+ }
+ if (FLAG_trace_thread_interrupter) {
+ OS::Print("ThreadInterrupter thread exiting.\n");
+ }
+ {
+ // Signal to main thread we are exiting.
+ MonitorLocker shutdown_ml(monitor_);
+ thread_running_ = false;
+ shutdown_ml.Notify();
+ }
+}
+
+} // namespace dart
« no previous file with comments | « runtime/vm/thread_interrupter.h ('k') | runtime/vm/thread_interrupter_android.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698