VM "cherry-picks" to fix memory leaks in 1.12:
Cherry-picks and associated conflict resolution for:
664742f0162a13e0d60c65de0a713df8acd7d8e9
d0b03eebbd63261e7beb8413aa9ce9dfa2a862cc
3aa62d03fcbffc2f2a77f05d2c8e975b75a56d76
74e02785b0e534e469668ce2cabced7d26ec55b1
028c9952959be3b1f275c3cfbe805ab80c49f07a
=====
Keep StoreBuffer::global_mutex_ and global_empty_ alive.
At Dart::CleanUp time, there may still be threads around that access their store buffer, so we need to keep these around until @zanderso lands the clean shutdown CL.
We should stop leaking them once we cleanly stop all threads at VM shutdown (see issue 23844).
BUG=https://github.com/dart-lang/sdk/issues/24169
R=iposva@google.com
Review URL: https://codereview.chromium.org//1305123003 .
Conflicts:
runtime/vm/dart.cc
Fix reuse of free store buffer blocks from the global cache of free blocks.
Add StoreBuffer::ShutDown to delete global cache of free blocks on VM shutdown.
BUG=https://github.com/dart-lang/sdk/issues/24129
R=iposva@google.com
Review URL: https://codereview.chromium.org//1309603003 .
Conflicts:
runtime/vm/dart.cc
- Prevent interrupting the NULL thread when the isolate is exiting from within a thread.
BUG=
Review URL: https://codereview.chromium.org//1307833002 .
- Initialize fields used in the profiler interrupts.
BUG=
R=koda@google.com
Review URL: https://codereview.chromium.org//1304043003 .
Completely remove InterruptableThreadState
- InterruptableThreadState is gone.
- Moved InterruptableThreadState fields directly into Thread.
- Iterate over all threads in an isolate when profiling.
- Still only sample the mutator thread.
Fix ThreadRegistry leak
- When deleting a Thread, iterate over all isolates and remove it from the isolate's thread registry.
R=iposva@google.com
Review URL: https://codereview.chromium.org//1293253005 .
Conflicts:
runtime/vm/isolate.h
runtime/vm/thread.cc
runtime/vm/thread.h
runtime/vm/thread_interrupter.cc
runtime/vm/thread_interrupter_android.cc
runtime/vm/thread_interrupter_linux.cc
runtime/vm/thread_interrupter_macos.cc
- Fix leak in TimelineEventRecorder.
- Fix leak in ThreadRegistry.
patch from issue 1305353003 at patchset 1 (http://crrev.com/1305353003#ps1)
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index 60439a2..8718c00 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -23,6 +23,7 @@
#include "vm/service_isolate.h"
#include "vm/simulator.h"
#include "vm/snapshot.h"
+#include "vm/store_buffer.h"
#include "vm/stub_code.h"
#include "vm/symbols.h"
#include "vm/thread_interrupter.h"
@@ -211,6 +212,7 @@
vm_isolate_ = NULL;
TargetCPUFeatures::Cleanup();
+ StoreBuffer::ShutDown();
#endif
Profiler::Shutdown();
diff --git a/runtime/vm/growable_array.h b/runtime/vm/growable_array.h
index 045c3e5..1e7c204 100644
--- a/runtime/vm/growable_array.h
+++ b/runtime/vm/growable_array.h
@@ -99,6 +99,17 @@
}
}
+ // Swap entries |i| and |j|.
+ void Swap(intptr_t i, intptr_t j) {
+ ASSERT(i >= 0);
+ ASSERT(j >= 0);
+ ASSERT(i < length_);
+ ASSERT(j < length_);
+ T temp = data_[i];
+ data_[i] = data_[j];
+ data_[j] = temp;
+ }
+
// The content is uninitialized after calling it.
void SetLength(intptr_t new_length);
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 764d21a..933afd7 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1666,8 +1666,8 @@
}
void Isolate::RemoveTimelineEventRecorder() {
- SetTimelineEventRecorder(NULL);
delete timeline_event_recorder_;
+ SetTimelineEventRecorder(NULL);
}
@@ -1789,14 +1789,28 @@
// Paused at start / exit . Don't tick.
return 0;
}
- InterruptableThreadState* state = thread_state();
- if (state == NULL) {
- // Isolate is not scheduled on a thread.
+ // Make sure that the isolate's mutator thread does not change behind our
+ // backs. Otherwise we find the entry in the registry and end up reading
+ // the field again. Only by that time it has been reset to NULL because the
+ // thread was in process of exiting the isolate.
+ Thread* mutator = mutator_thread_;
+ if (mutator == NULL) {
+ // No active mutator.
ProfileIdle();
return 1;
}
- ASSERT(state->id != OSThread::kInvalidThreadId);
- ThreadInterrupter::InterruptThread(state);
+
+ // TODO(johnmccutchan): Sample all threads, not just the mutator thread.
+ // TODO(johnmccutchan): Keep a global list of threads and use that
+ // instead of Isolate.
+ ThreadRegistry::EntryIterator it(thread_registry());
+ while (it.HasNext()) {
+ const ThreadRegistry::Entry& entry = it.Next();
+ if (entry.thread == mutator) {
+ ThreadInterrupter::InterruptThread(mutator);
+ break;
+ }
+ }
return 1;
}
@@ -1949,19 +1963,6 @@
}
-#if defined(DEBUG)
-void Isolate::CheckForDuplicateThreadState(InterruptableThreadState* state) {
- MonitorLocker ml(isolates_list_monitor_);
- ASSERT(state != NULL);
- Isolate* current = isolates_list_head_;
- while (current) {
- ASSERT(current->thread_state() != state);
- current = current->next_;
- }
-}
-#endif
-
-
template<class T>
T* Isolate::AllocateReusableHandle() {
T* handle = reinterpret_cast<T*>(reusable_handles_.AllocateScopedHandle());
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index c43a3fa..9621311 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -632,15 +632,6 @@
void PrintJSON(JSONStream* stream, bool ref = true);
- void set_thread_state(InterruptableThreadState* state) {
- ASSERT((thread_state_ == NULL) || (state == NULL));
- thread_state_ = state;
- }
-
- InterruptableThreadState* thread_state() const {
- return thread_state_;
- }
-
CompilerStats* compiler_stats() {
return compiler_stats_;
}
@@ -925,7 +916,6 @@
// Manage list of existing isolates.
static void AddIsolateTolist(Isolate* isolate);
static void RemoveIsolateFromList(Isolate* isolate);
- static void CheckForDuplicateThreadState(InterruptableThreadState* state);
static Monitor* isolates_list_monitor_; // Protects isolates_list_head_
static Isolate* isolates_list_head_;
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index ff0edc7..0ace0cc 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -169,7 +169,8 @@
if (profiler_data == NULL) {
return;
}
- ThreadInterrupter::Register(RecordSampleInterruptCallback, isolate);
+ Thread* thread = Thread::Current();
+ thread->SetThreadInterrupter(RecordSampleInterruptCallback, isolate);
ThreadInterrupter::WakeUp();
}
@@ -182,7 +183,8 @@
return;
}
ASSERT(initialized_);
- ThreadInterrupter::Unregister();
+ Thread* thread = Thread::Current();
+ thread->SetThreadInterrupter(NULL, NULL);
}
diff --git a/runtime/vm/store_buffer.cc b/runtime/vm/store_buffer.cc
index f4fbad7..fee1281 100644
--- a/runtime/vm/store_buffer.cc
+++ b/runtime/vm/store_buffer.cc
@@ -26,6 +26,12 @@
}
+void StoreBuffer::ShutDown() {
+ delete global_empty_;
+ delete global_mutex_;
+}
+
+
StoreBuffer::StoreBuffer() : mutex_(new Mutex()) {
}
@@ -105,7 +111,7 @@
{
MutexLocker ml(global_mutex_);
if (!global_empty_->IsEmpty()) {
- global_empty_->Pop();
+ return global_empty_->Pop();
}
}
return new StoreBufferBlock();
diff --git a/runtime/vm/store_buffer.h b/runtime/vm/store_buffer.h
index b1bd387..12a0535 100644
--- a/runtime/vm/store_buffer.h
+++ b/runtime/vm/store_buffer.h
@@ -79,6 +79,7 @@
StoreBuffer();
~StoreBuffer();
static void InitOnce();
+ static void ShutDown();
// Interrupt when crossing this threshold of non-empty blocks in the buffer.
static const intptr_t kMaxNonEmpty = 100;
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index aa4f67e..09a0774 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -22,6 +22,24 @@
ThreadLocalKey Thread::thread_key_ = OSThread::kUnsetThreadLocalKey;
+// Remove |thread| from each isolate's thread registry.
+class ThreadPruner : public IsolateVisitor {
+ public:
+ explicit ThreadPruner(Thread* thread)
+ : thread_(thread) {
+ ASSERT(thread_ != NULL);
+ }
+
+ void VisitIsolate(Isolate* isolate) {
+ ThreadRegistry* registry = isolate->thread_registry();
+ ASSERT(registry != NULL);
+ registry->PruneThread(thread_);
+ }
+ private:
+ Thread* thread_;
+};
+
+
static void DeleteThread(void* thread) {
delete reinterpret_cast<Thread*>(thread);
}
@@ -30,6 +48,9 @@
Thread::~Thread() {
// We should cleanly exit any isolate before destruction.
ASSERT(isolate_ == NULL);
+ // Clear |this| from all isolate's thread registry.
+ ThreadPruner pruner(this);
+ Isolate::VisitIsolates(&pruner);
}
@@ -38,8 +59,11 @@
thread_key_ = OSThread::CreateThreadLocal(DeleteThread);
ASSERT(thread_key_ != OSThread::kUnsetThreadLocalKey);
ASSERT(Thread::Current() == NULL);
- // Postpone initialization of VM constants for this first thread.
- SetCurrent(new Thread(false));
+ // Allocate a new Thread and postpone initialization of VM constants for
+ // this first thread.
+ Thread* thread = new Thread(false);
+ // Verify that current thread was set.
+ ASSERT(Thread::Current() == thread);
}
@@ -58,7 +82,10 @@
void Thread::EnsureInit() {
if (Thread::Current() == NULL) {
- SetCurrent(new Thread());
+ // Allocate a new Thread.
+ Thread* thread = new Thread();
+ // Verify that current thread was set.
+ ASSERT(Thread::Current() == thread);
}
}
@@ -75,7 +102,11 @@
Thread::Thread(bool init_vm_constants)
- : isolate_(NULL),
+ : id_(OSThread::GetCurrentThreadId()),
+ thread_interrupt_callback_(NULL),
+ thread_interrupt_data_(NULL),
+ isolate_(NULL),
+ heap_(NULL),
store_buffer_block_(NULL) {
ClearState();
#define DEFAULT_INIT(type_name, member_name, init_expr, default_init_value) \
@@ -85,6 +116,7 @@
if (init_vm_constants) {
InitVMConstants();
}
+ SetCurrent(this);
}
@@ -126,23 +158,14 @@
ASSERT(!isolate->HasMutatorThread());
thread->isolate_ = isolate;
isolate->MakeCurrentThreadMutator(thread);
- // TODO(koda): Migrate thread_state_ and profile_data_ to Thread, to allow
- // helper threads concurrent with mutator.
- ASSERT(isolate->thread_state() == NULL);
- InterruptableThreadState* thread_state =
- ThreadInterrupter::GetCurrentThreadState();
-#if defined(DEBUG)
- Isolate::CheckForDuplicateThreadState(thread_state);
-#endif
- ASSERT(thread_state != NULL);
- Profiler::BeginExecution(isolate);
- isolate->set_thread_state(thread_state);
isolate->set_vm_tag(VMTag::kVMTagId);
ASSERT(thread->store_buffer_block_ == NULL);
thread->store_buffer_block_ = isolate->store_buffer()->PopBlock();
ASSERT(isolate->heap() != NULL);
thread->heap_ = isolate->heap();
thread->Schedule(isolate);
+ // TODO(koda): Migrate profiler interface to use Thread.
+ Profiler::BeginExecution(isolate);
}
@@ -151,6 +174,7 @@
// TODO(koda): Audit callers; they should know whether they're in an isolate.
if (thread == NULL || thread->isolate() == NULL) return;
Isolate* isolate = thread->isolate();
+ Profiler::EndExecution(isolate);
thread->Unschedule();
StoreBufferBlock* block = thread->store_buffer_block_;
thread->store_buffer_block_ = NULL;
@@ -160,8 +184,6 @@
} else {
isolate->set_vm_tag(VMTag::kLoadWaitTagId);
}
- isolate->set_thread_state(NULL);
- Profiler::EndExecution(isolate);
isolate->ClearMutatorThread();
thread->isolate_ = NULL;
ASSERT(Isolate::Current() == NULL);
@@ -245,6 +267,32 @@
}
+void Thread::SetThreadInterrupter(ThreadInterruptCallback callback,
+ void* data) {
+ ASSERT(Thread::Current() == this);
+ thread_interrupt_callback_ = callback;
+ thread_interrupt_data_ = data;
+}
+
+
+bool Thread::IsThreadInterrupterEnabled(ThreadInterruptCallback* callback,
+ void** data) const {
+#if defined(TARGET_OS_WINDOWS)
+ // On Windows we expect this to be called from the thread interrupter thread.
+ ASSERT(id() != OSThread::GetCurrentThreadId());
+#else
+ // On posix platforms, we expect this to be called from signal handler.
+ ASSERT(id() == OSThread::GetCurrentThreadId());
+#endif
+ ASSERT(callback != NULL);
+ ASSERT(data != NULL);
+ *callback = thread_interrupt_callback_;
+ *data = thread_interrupt_data_;
+ return (*callback != NULL) &&
+ (*data != NULL);
+}
+
+
bool Thread::CanLoadFromThread(const Object& object) {
#define CHECK_OBJECT(type_name, member_name, expr, default_init_value) \
if (object.raw() == expr) return true;
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index 2b2d343..557b862 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -36,6 +36,26 @@
CACHED_VM_OBJECTS_LIST(V) \
CACHED_ADDRESSES_LIST(V)
+struct InterruptedThreadState {
+ ThreadId tid;
+ uintptr_t pc;
+ uintptr_t csp;
+ uintptr_t dsp;
+ uintptr_t fp;
+ uintptr_t lr;
+};
+
+// When a thread is interrupted the thread specific interrupt callback will be
+// invoked. Each callback is given an InterruptedThreadState and the user data
+// pointer. When inside a thread interrupt callback doing any of the following
+// is forbidden:
+// * Accessing TLS -- Because on Windows the callback will be running in a
+// different thread.
+// * Allocating memory -- Because this takes locks which may already be held,
+// resulting in a dead lock.
+// * Taking a lock -- See above.
+typedef void (*ThreadInterruptCallback)(const InterruptedThreadState& state,
+ void* data);
// A VM thread; may be executing Dart code or performing helper tasks like
// garbage collection or compilation. The Thread structure associated with
@@ -204,9 +224,22 @@
static bool CanLoadFromThread(const Object& object);
static intptr_t OffsetFromThread(const Object& object);
+ ThreadId id() const {
+ ASSERT(id_ != OSThread::kInvalidThreadId);
+ return id_;
+ }
+
+ void SetThreadInterrupter(ThreadInterruptCallback callback, void* data);
+
+ bool IsThreadInterrupterEnabled(ThreadInterruptCallback* callback,
+ void** data) const;
+
private:
static ThreadLocalKey thread_key_;
+ const ThreadId id_;
+ ThreadInterruptCallback thread_interrupt_callback_;
+ void* thread_interrupt_data_;
Isolate* isolate_;
Heap* heap_;
State state_;
diff --git a/runtime/vm/thread_interrupter.cc b/runtime/vm/thread_interrupter.cc
index 10cff89..b28bf25 100644
--- a/runtime/vm/thread_interrupter.cc
+++ b/runtime/vm/thread_interrupter.cc
@@ -153,95 +153,6 @@
}
}
-// Register the currently running thread for interrupts. If the current thread
-// is already registered, callback and data will be updated.
-InterruptableThreadState* ThreadInterrupter::Register(
- ThreadInterruptCallback callback, void* data) {
- if (shutdown_) {
- return NULL;
- }
- ASSERT(initialized_);
- InterruptableThreadState* state = _EnsureThreadStateCreated();
- // Set callback and data.
- UpdateStateObject(callback, data);
- return state;
-}
-
-
-// Unregister the currently running thread for interrupts.
-void ThreadInterrupter::Unregister() {
- if (shutdown_) {
- return;
- }
- ASSERT(initialized_);
- _EnsureThreadStateCreated();
- // Clear callback and data.
- UpdateStateObject(NULL, NULL);
-}
-
-
-InterruptableThreadState* ThreadInterrupter::_EnsureThreadStateCreated() {
- InterruptableThreadState* state = CurrentThreadState();
- if (state == NULL) {
- // Create thread state object lazily.
- ThreadId current_thread = OSThread::GetCurrentThreadId();
- if (FLAG_trace_thread_interrupter) {
- intptr_t tid = OSThread::ThreadIdToIntPtr(current_thread);
- OS::Print("ThreadInterrupter Tracking %p\n",
- reinterpret_cast<void*>(tid));
- }
- // Note: We currently do not free a thread's InterruptableThreadState.
- state = new InterruptableThreadState();
- ASSERT(state != NULL);
- state->callback = NULL;
- state->data = NULL;
- state->id = current_thread;
- SetCurrentThreadState(state);
- }
- return state;
-}
-
-
-void ThreadInterrupter::UpdateStateObject(ThreadInterruptCallback callback,
- void* data) {
- InterruptableThreadState* state = CurrentThreadState();
- ThreadId current_thread = OSThread::GetCurrentThreadId();
- ASSERT(state != NULL);
- ASSERT(OSThread::Compare(state->id, OSThread::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 = OSThread::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));
- }
- }
-}
-
-
-InterruptableThreadState* ThreadInterrupter::GetCurrentThreadState() {
- return _EnsureThreadStateCreated();
-}
-
-
-InterruptableThreadState* ThreadInterrupter::CurrentThreadState() {
- InterruptableThreadState* state = reinterpret_cast<InterruptableThreadState*>(
- OSThread::GetThreadLocal(thread_state_key_));
- return state;
-}
-
-
-void ThreadInterrupter::SetCurrentThreadState(InterruptableThreadState* state) {
- OSThread::SetThreadLocal(thread_state_key_,
- reinterpret_cast<uword>(state));
-}
-
void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data) {
// NoOp.
diff --git a/runtime/vm/thread_interrupter.h b/runtime/vm/thread_interrupter.h
index 2d3d1b3..2e84b13 100644
--- a/runtime/vm/thread_interrupter.h
+++ b/runtime/vm/thread_interrupter.h
@@ -8,37 +8,10 @@
#include "vm/allocation.h"
#include "vm/signal_handler.h"
#include "vm/os_thread.h"
-
+#include "vm/thread.h"
namespace dart {
-struct InterruptedThreadState {
- ThreadId tid;
- uintptr_t pc;
- uintptr_t csp;
- uintptr_t dsp;
- uintptr_t fp;
- uintptr_t lr;
-};
-
-// When a thread is interrupted the thread specific interrupt callback will be
-// invoked. Each callback is given an InterruptedThreadState and the user data
-// pointer. When inside a thread interrupt callback doing any of the following
-// is forbidden:
-// * Accessing TLS.
-// * Allocating memory.
-// * Taking a lock.
-typedef void (*ThreadInterruptCallback)(const InterruptedThreadState& state,
- void* data);
-
-// State stored per registered thread.
-class InterruptableThreadState {
- public:
- ThreadId id;
- ThreadInterruptCallback callback;
- void* data;
-};
-
class ThreadInterrupter : public AllStatic {
public:
static void InitOnce();
@@ -54,19 +27,13 @@
// Register the currently running thread for interrupts. If the current thread
// is already registered, callback and data will be updated.
- static InterruptableThreadState* Register(ThreadInterruptCallback callback,
- void* data);
+ static void Register(ThreadInterruptCallback callback, void* data);
+
// Unregister the currently running thread for interrupts.
static void Unregister();
- // Get the current thread state. Will create a thread state if one hasn't
- // been allocated.
- static InterruptableThreadState* GetCurrentThreadState();
- // Get the current thread state. Will not create one if one doesn't exist.
- static InterruptableThreadState* CurrentThreadState();
-
// Interrupt a thread.
- static void InterruptThread(InterruptableThreadState* thread_state);
+ static void InterruptThread(Thread* thread);
private:
static const intptr_t kMaxThreads = 4096;
@@ -83,11 +50,8 @@
return current_wait_time_ == Monitor::kNoTimeout;
}
- static InterruptableThreadState* _EnsureThreadStateCreated();
static void UpdateStateObject(ThreadInterruptCallback callback, void* data);
- static void SetCurrentThreadState(InterruptableThreadState* state);
-
static void ThreadMain(uword parameters);
static void InstallSignalHandler();
diff --git a/runtime/vm/thread_interrupter_android.cc b/runtime/vm/thread_interrupter_android.cc
index fd27d53..5c4092d 100644
--- a/runtime/vm/thread_interrupter_android.cc
+++ b/runtime/vm/thread_interrupter_android.cc
@@ -24,33 +24,37 @@
if (signal != SIGPROF) {
return;
}
- InterruptableThreadState* state = ThreadInterrupter::CurrentThreadState();
- if ((state == NULL) || (state->callback == NULL)) {
- // No interrupter state or callback.
+ Thread* thread = Thread::Current();
+ if (thread == NULL) {
return;
}
- ASSERT(OSThread::Compare(state->id, OSThread::GetCurrentThreadId()));
+ ThreadInterruptCallback callback = NULL;
+ void* callback_data = NULL;
+ if (!thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
+ return;
+ }
// Extract thread state.
ucontext_t* context = reinterpret_cast<ucontext_t*>(context_);
mcontext_t mcontext = context->uc_mcontext;
InterruptedThreadState its;
- its.tid = state->id;
+ its.tid = thread->id();
its.pc = SignalHandler::GetProgramCounter(mcontext);
its.fp = SignalHandler::GetFramePointer(mcontext);
its.csp = SignalHandler::GetCStackPointer(mcontext);
its.dsp = SignalHandler::GetDartStackPointer(mcontext);
its.lr = SignalHandler::GetLinkRegister(mcontext);
- state->callback(its, state->data);
+ callback(its, callback_data);
}
};
-void ThreadInterrupter::InterruptThread(InterruptableThreadState* state) {
+void ThreadInterrupter::InterruptThread(Thread* thread) {
if (FLAG_trace_thread_interrupter) {
OS::Print("ThreadInterrupter interrupting %p\n",
- reinterpret_cast<void*>(state->id));
+ reinterpret_cast<void*>(thread->id()));
}
- syscall(__NR_tgkill, getpid(), state->id, SIGPROF);
+ int result = syscall(__NR_tgkill, getpid(), thread->id(), SIGPROF);
+ ASSERT(result == 0);
}
diff --git a/runtime/vm/thread_interrupter_linux.cc b/runtime/vm/thread_interrupter_linux.cc
index 5ff3c92..d7b9b85 100644
--- a/runtime/vm/thread_interrupter_linux.cc
+++ b/runtime/vm/thread_interrupter_linux.cc
@@ -22,33 +22,37 @@
if (signal != SIGPROF) {
return;
}
- InterruptableThreadState* state = ThreadInterrupter::CurrentThreadState();
- if ((state == NULL) || (state->callback == NULL)) {
- // No interrupter state or callback.
+ Thread* thread = Thread::Current();
+ if (thread == NULL) {
return;
}
- ASSERT(OSThread::Compare(state->id, OSThread::GetCurrentThreadId()));
+ ThreadInterruptCallback callback = NULL;
+ void* callback_data = NULL;
+ if (!thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
+ return;
+ }
// Extract thread state.
ucontext_t* context = reinterpret_cast<ucontext_t*>(context_);
mcontext_t mcontext = context->uc_mcontext;
InterruptedThreadState its;
- its.tid = state->id;
+ its.tid = thread->id();
its.pc = SignalHandler::GetProgramCounter(mcontext);
its.fp = SignalHandler::GetFramePointer(mcontext);
its.csp = SignalHandler::GetCStackPointer(mcontext);
its.dsp = SignalHandler::GetDartStackPointer(mcontext);
its.lr = SignalHandler::GetLinkRegister(mcontext);
- state->callback(its, state->data);
+ callback(its, callback_data);
}
};
-void ThreadInterrupter::InterruptThread(InterruptableThreadState* state) {
+void ThreadInterrupter::InterruptThread(Thread* thread) {
if (FLAG_trace_thread_interrupter) {
OS::Print("ThreadInterrupter interrupting %p\n",
- reinterpret_cast<void*>(state->id));
+ reinterpret_cast<void*>(thread->id()));
}
- pthread_kill(state->id, SIGPROF);
+ int result = pthread_kill(thread->id(), SIGPROF);
+ ASSERT(result == 0);
}
diff --git a/runtime/vm/thread_interrupter_macos.cc b/runtime/vm/thread_interrupter_macos.cc
index b5beeeb..f1ff45e 100644
--- a/runtime/vm/thread_interrupter_macos.cc
+++ b/runtime/vm/thread_interrupter_macos.cc
@@ -22,32 +22,36 @@
if (signal != SIGPROF) {
return;
}
- InterruptableThreadState* state = ThreadInterrupter::CurrentThreadState();
- if ((state == NULL) || (state->callback == NULL)) {
- // No interrupter state or callback.
+ Thread* thread = Thread::Current();
+ if (thread == NULL) {
return;
}
- ASSERT(OSThread::Compare(state->id, OSThread::GetCurrentThreadId()));
+ ThreadInterruptCallback callback = NULL;
+ void* callback_data = NULL;
+ if (!thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
+ return;
+ }
// Extract thread state.
ucontext_t* context = reinterpret_cast<ucontext_t*>(context_);
mcontext_t mcontext = context->uc_mcontext;
InterruptedThreadState its;
- its.tid = state->id;
+ its.tid = thread->id();
its.pc = SignalHandler::GetProgramCounter(mcontext);
its.fp = SignalHandler::GetFramePointer(mcontext);
its.csp = SignalHandler::GetCStackPointer(mcontext);
its.dsp = SignalHandler::GetDartStackPointer(mcontext);
its.lr = SignalHandler::GetLinkRegister(mcontext);
- state->callback(its, state->data);
+ callback(its, callback_data);
}
};
-void ThreadInterrupter::InterruptThread(InterruptableThreadState* state) {
+void ThreadInterrupter::InterruptThread(Thread* thread) {
if (FLAG_trace_thread_interrupter) {
- OS::Print("ThreadInterrupter interrupting %p\n", state->id);
+ OS::Print("ThreadInterrupter interrupting %p\n", thread->id());
}
- pthread_kill(state->id, SIGPROF);
+ int result = pthread_kill(thread->id(), SIGPROF);
+ ASSERT(result == 0);
}
diff --git a/runtime/vm/thread_interrupter_test.cc b/runtime/vm/thread_interrupter_test.cc
index 4d69edd..aeb9ff1 100644
--- a/runtime/vm/thread_interrupter_test.cc
+++ b/runtime/vm/thread_interrupter_test.cc
@@ -17,11 +17,12 @@
static void InterruptTest(const intptr_t run_time, const intptr_t period) {
const double allowed_error = 0.25; // +/- 25%
intptr_t count = 0;
- ThreadInterrupter::Unregister();
+ Thread::EnsureInit();
+ Thread* thread = Thread::Current();
+ thread->SetThreadInterrupter(IncrementCallback, &count);
ThreadInterrupter::SetInterruptPeriod(period);
- ThreadInterrupter::Register(IncrementCallback, &count);
OS::Sleep(run_time * kMillisecondsPerSecond);
- ThreadInterrupter::Unregister();
+ thread->SetThreadInterrupter(NULL, NULL);
intptr_t run_time_micros = run_time * kMicrosecondsPerSecond;
intptr_t expected_interrupts = run_time_micros / period;
intptr_t error = allowed_error * expected_interrupts;
diff --git a/runtime/vm/thread_interrupter_win.cc b/runtime/vm/thread_interrupter_win.cc
index 07feed0..72220ed 100644
--- a/runtime/vm/thread_interrupter_win.cc
+++ b/runtime/vm/thread_interrupter_win.cc
@@ -51,57 +51,55 @@
}
- static void Interrupt(InterruptableThreadState* state) {
- ASSERT(!OSThread::Compare(GetCurrentThreadId(), state->id));
+ static void Interrupt(Thread* thread) {
+ ASSERT(!OSThread::Compare(GetCurrentThreadId(), thread->id()));
HANDLE handle = OpenThread(THREAD_GET_CONTEXT |
THREAD_QUERY_INFORMATION |
THREAD_SUSPEND_RESUME,
false,
- state->id);
+ thread->id());
ASSERT(handle != NULL);
DWORD result = SuspendThread(handle);
if (result == kThreadError) {
if (FLAG_trace_thread_interrupter) {
- OS::Print("ThreadInterrupted failed to suspend thread %p\n",
- reinterpret_cast<void*>(state->id));
+ OS::Print("ThreadInterrupter failed to suspend thread %p\n",
+ reinterpret_cast<void*>(thread->id()));
}
CloseHandle(handle);
return;
}
InterruptedThreadState its;
- its.tid = state->id;
+ its.tid = thread->id();
if (!GrabRegisters(handle, &its)) {
// Failed to get thread registers.
ResumeThread(handle);
if (FLAG_trace_thread_interrupter) {
- OS::Print("ThreadInterrupted failed to get registers for %p\n",
- reinterpret_cast<void*>(state->id));
+ OS::Print("ThreadInterrupter failed to get registers for %p\n",
+ reinterpret_cast<void*>(thread->id()));
}
CloseHandle(handle);
return;
}
- if (state->callback == NULL) {
- // No callback registered.
- ResumeThread(handle);
- CloseHandle(handle);
- return;
+ ThreadInterruptCallback callback = NULL;
+ void* callback_data = NULL;
+ if (thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
+ callback(its, callback_data);
}
- state->callback(its, state->data);
ResumeThread(handle);
CloseHandle(handle);
}
};
-void ThreadInterrupter::InterruptThread(InterruptableThreadState* state) {
+void ThreadInterrupter::InterruptThread(Thread* thread) {
if (FLAG_trace_thread_interrupter) {
OS::Print("ThreadInterrupter suspending %p\n",
- reinterpret_cast<void*>(state->id));
+ reinterpret_cast<void*>(thread->id()));
}
- ThreadInterrupterWin::Interrupt(state);
+ ThreadInterrupterWin::Interrupt(thread);
if (FLAG_trace_thread_interrupter) {
OS::Print("ThreadInterrupter resuming %p\n",
- reinterpret_cast<void*>(state->id));
+ reinterpret_cast<void*>(thread->id()));
}
}
diff --git a/runtime/vm/thread_registry.cc b/runtime/vm/thread_registry.cc
index 8ade009..23e22b7 100644
--- a/runtime/vm/thread_registry.cc
+++ b/runtime/vm/thread_registry.cc
@@ -9,6 +9,11 @@
namespace dart {
+ThreadRegistry::~ThreadRegistry() {
+ delete monitor_;
+}
+
+
void ThreadRegistry::SafepointThreads() {
MonitorLocker ml(monitor_);
// First wait for any older rounds that are still in progress.
@@ -44,6 +49,74 @@
}
+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;
+ }
+ if (found_index != (length - 1)) {
+ // Swap with last entry.
+ entries_.Swap(found_index, length - 1);
+ }
+ entries_.RemoveLast();
+}
+
+
+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_) {
diff --git a/runtime/vm/thread_registry.h b/runtime/vm/thread_registry.h
index f8f324d..abe669a 100644
--- a/runtime/vm/thread_registry.h
+++ b/runtime/vm/thread_registry.h
@@ -23,6 +23,8 @@
remaining_(0),
round_(0) {}
+ ~ThreadRegistry();
+
// 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
@@ -125,13 +127,33 @@
}
}
- private:
+ void PruneThread(Thread* thread);
+
struct Entry {
Thread* thread;
bool scheduled;
Thread::State state;
};
+ class EntryIterator {
+ public:
+ explicit EntryIterator(ThreadRegistry* registry);
+ ~EntryIterator();
+
+ // Returns false when there are no more entries.
+ bool HasNext() const;
+
+ // Returns the next entry and moves forward.
+ const Entry& Next();
+
+ private:
+ void Reset(ThreadRegistry* registry);
+
+ intptr_t index_;
+ ThreadRegistry* registry_;
+ };
+
+ private:
// 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.
diff --git a/runtime/vm/thread_test.cc b/runtime/vm/thread_test.cc
index d1db1d3..39bec07 100644
--- a/runtime/vm/thread_test.cc
+++ b/runtime/vm/thread_test.cc
@@ -38,9 +38,10 @@
// This unit test case needs a running isolate.
Dart_CreateIsolate(
NULL, NULL, bin::isolate_snapshot_buffer, NULL, NULL, NULL);
- Isolate* isolate = Isolate::Current();
+ Thread* thread = Thread::Current();
+ Isolate* isolate = thread->isolate();
// Thread interrupter interferes with this test, disable interrupts.
- isolate->set_thread_state(NULL);
+ thread->SetThreadInterrupter(NULL, NULL);
Profiler::EndExecution(isolate);
Monitor* monitor = new Monitor();
monitor->Enter();