Version 1.12.0-dev.5.9
Cherry-pick 1d537028f7cbe026fb682804fb13c72aa3261ae5 to dev
Cherry-pick 6e0b2418c7024406f603c2c775d320484355a449 to dev
Cherry-pick b569deac480aeced9fccbb802d47cfdcd8691894 to dev
Cherry-pick 664742f0162a13e0d60c65de0a713df8acd7d8e9 to dev
Cherry-pick f07cec063c4ac380119a628f25573db9a21d8634 to dev
Cherry-pick d0b03eebbd63261e7beb8413aa9ce9dfa2a862cc to dev
Cherry-pick 3aa62d03fcbffc2f2a77f05d2c8e975b75a56d76 to dev
Cherry-pick 74e02785b0e534e469668ce2cabced7d26ec55b1 to dev
Cherry-pick 028c9952959be3b1f275c3cfbe805ab80c49f07a to dev
diff --git a/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart b/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart
index e9ceabb..c54bebd 100644
--- a/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart
+++ b/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart
@@ -799,8 +799,6 @@
elements.getGetterSelectorInComplexSendSet(node);
TypeMask getterMask =
elements.getGetterTypeMaskInComplexSendSet(node);
- Selector operatorSelector =
- elements.getOperatorSelectorInComplexSendSet(node);
TypeMask operatorMask =
elements.getOperatorTypeMaskInComplexSendSet(node);
Selector setterSelector = elements.getSelector(node);
@@ -859,53 +857,53 @@
node, element, setterSelector, setterMask, receiverType, rhsType,
node.arguments.head);
} else {
- // [: foo++ :] or [: foo += 1 :].
- ArgumentsTypes operatorArguments = new ArgumentsTypes<T>([rhsType], null);
+ // [foo ??= bar], [: foo++ :] or [: foo += 1 :].
T getterType;
T newType;
- if (Elements.isErroneous(element)) {
- getterType = types.dynamicType;
- newType = types.dynamicType;
- } else if (Elements.isStaticOrTopLevelField(element)) {
+
+ if (Elements.isErroneous(element)) return types.dynamicType;
+
+ if (Elements.isStaticOrTopLevelField(element)) {
Element getterElement = elements[node.selector];
getterType = handleStaticSend(
node, getterSelector, getterMask, getterElement, null);
+ } else if (Elements.isUnresolved(element)
+ || element.isSetter
+ || element.isField) {
+ getterType = handleDynamicSend(
+ node, getterSelector, getterMask, receiverType, null);
+ } else if (element.isLocal) {
+ LocalElement local = element;
+ getterType = locals.use(local);
+ } else {
+ // Bogus SendSet, for example [: myMethod += 42 :].
+ getterType = types.dynamicType;
+ }
+
+ if (op == '??=') {
+ newType = types.allocateDiamondPhi(getterType, rhsType);
+ } else {
+ Selector operatorSelector =
+ elements.getOperatorSelectorInComplexSendSet(node);
newType = handleDynamicSend(
node, operatorSelector, operatorMask,
- getterType, operatorArguments);
+ getterType, new ArgumentsTypes<T>([rhsType], null));
+ }
+
+ if (Elements.isStaticOrTopLevelField(element)) {
handleStaticSend(
node, setterSelector, setterMask, element,
new ArgumentsTypes<T>([newType], null));
} else if (Elements.isUnresolved(element)
|| element.isSetter
|| element.isField) {
- getterType = handleDynamicSend(
- node, getterSelector, getterMask, receiverType, null);
- newType = handleDynamicSend(
- node, operatorSelector, operatorMask,
- getterType, operatorArguments);
handleDynamicSend(node, setterSelector, setterMask, receiverType,
new ArgumentsTypes<T>([newType], null));
} else if (element.isLocal) {
- LocalElement local = element;
- getterType = locals.use(local);
- newType = handleDynamicSend(
- node, operatorSelector, operatorMask,
- getterType, operatorArguments);
locals.update(element, newType, node);
- } else {
- // Bogus SendSet, for example [: myMethod += 42 :].
- getterType = types.dynamicType;
- newType = handleDynamicSend(
- node, operatorSelector, operatorMask,
- getterType, operatorArguments);
}
- if (node.isPostfix) {
- return getterType;
- } else {
- return newType;
- }
+ return node.isPostfix ? getterType : newType;
}
}
@@ -932,12 +930,18 @@
getterMask,
receiverType,
new ArgumentsTypes<T>([indexType], null));
- T returnType = handleDynamicSend(
- node,
- operatorSelector,
- operatorMask,
- getterType,
- new ArgumentsTypes<T>([rhsType], null));
+
+ T returnType;
+ if (node.isIfNullAssignment) {
+ returnType = types.allocateDiamondPhi(getterType, rhsType);
+ } else {
+ returnType = handleDynamicSend(
+ node,
+ operatorSelector,
+ operatorMask,
+ getterType,
+ new ArgumentsTypes<T>([rhsType], null));
+ }
handleDynamicSend(
node,
setterSelector,
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();
diff --git a/sdk/api_readme.md b/sdk/api_readme.md
index 250c823..c723990 100644
--- a/sdk/api_readme.md
+++ b/sdk/api_readme.md
@@ -1,9 +1,9 @@
Welcome to the Dart API reference documentation, covering the official Dart API
libraries. Some of the most fundamental Dart libraries include:
- * [dart:core](dart-core/index.html): Core functionality such as strings, numbers, collections, errors, dates, and URIs.
- * [dart:html](dart-html/index.html): DOM manipulation for web apps.
- * [dart:io](dart-io/index.html): I/O for command-line apps.
+ * [dart:core](dart-core/dart-core-library.html): Core functionality such as strings, numbers, collections, errors, dates, and URIs.
+ * [dart:html](dart-html/dart-html-library.html): DOM manipulation for web apps.
+ * [dart:io](dart-io/dart-io-library.html): I/O for command-line apps.
Except for `dart:core`, you must import a library before you can use it. Here's
an example of importing `dart:html` and `dart:math`:
diff --git a/sdk/lib/core/resource.dart b/sdk/lib/core/resource.dart
index 9bb88d1..17bdebc 100644
--- a/sdk/lib/core/resource.dart
+++ b/sdk/lib/core/resource.dart
@@ -7,6 +7,9 @@
/**
* A resource that can be read into the program.
*
+ * WARNING: This API is _experimental_,
+ * and it may be changed or removed in future releases
+ *
* A resource is data that can be located using a URI and read into
* the program at runtime.
* The URI may use the `package` scheme to read resources provided
diff --git a/tests/compiler/dart2js_extra/if_null2_test.dart b/tests/compiler/dart2js_extra/if_null2_test.dart
new file mode 100644
index 0000000..a9923a8
--- /dev/null
+++ b/tests/compiler/dart2js_extra/if_null2_test.dart
@@ -0,0 +1,15 @@
+// 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.
+
+/// Regression for #24134: inference was not tracking ??= correctly.
+library tests.compiler.dart2js_extra.if_null2_test;
+
+import "package:expect/expect.dart";
+
+main() {
+ var map;
+ map ??= {};
+ Expect.equals(0, map.length);
+ Expect.isTrue(map.length == 0);
+}
diff --git a/tests/compiler/dart2js_extra/if_null3_test.dart b/tests/compiler/dart2js_extra/if_null3_test.dart
new file mode 100644
index 0000000..dac99e0
--- /dev/null
+++ b/tests/compiler/dart2js_extra/if_null3_test.dart
@@ -0,0 +1,14 @@
+// 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.
+
+/// Regression for #24135: inference was not tracking `[]??=` correctly.
+library tests.compiler.dart2js_extra.if_null3_test;
+
+import "package:expect/expect.dart";
+
+void main() {
+ var map;
+ (((map ??= {})['key1'] ??= {})['key2'] ??= {})['key3'] = 'value';
+ Expect.equals('{key1: {key2: {key3: value}}}', '$map');
+}
diff --git a/tests/compiler/dart2js_extra/if_null_test.dart b/tests/compiler/dart2js_extra/if_null_test.dart
index deb9b64..5574643 100644
--- a/tests/compiler/dart2js_extra/if_null_test.dart
+++ b/tests/compiler/dart2js_extra/if_null_test.dart
@@ -2,7 +2,6 @@
// 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.
-// SharedOptions=--enable-null-aware-operators
import "package:expect/expect.dart";
@NoInline() @AssumeDynamic()
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index 4740960..0d39999 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -120,7 +120,6 @@
if_null_assignment_behavior_test/13: Crash # Issue 23491
if_null_assignment_behavior_test/14: Crash # Issue 23491
-nullaware_opt_test: Fail # Fails at e?.f ??= 200;
if_null_assignment_behavior_test/29: Crash # Issue 23611
prefix_assignment_test/01: Crash # Issue 23611
@@ -488,20 +487,6 @@
generic_field_mixin3_test: RuntimeError # Please triage this failure.
generic_instanceof_test: RuntimeError # Please triage this failure.
generic_native_test: RuntimeError # Please triage this failure.
-if_null_assignment_behavior_test/12: RuntimeError # Please triage this failure.
-if_null_assignment_behavior_test/28: RuntimeError # Please triage this failure.
-if_null_assignment_static_test/01: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/03: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/04: RuntimeError # v0.get$b is not a function
-if_null_assignment_static_test/05: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/29: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/31: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/32: RuntimeError # v0.get$b is not a function
-if_null_assignment_static_test/33: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/36: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/38: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/39: RuntimeError # v0.get$b is not a function
-if_null_assignment_static_test/40: RuntimeError # v0.get$a is not a function
infinite_switch_label_test: Crash # (switch (target){l0:... continue to a labeled switch case
instance_creation_in_function_annotation_test: Crash # (static Iterable<Str... cannot handle async/sync*/async* functions
instanceof2_test: RuntimeError # Please triage this failure.
diff --git a/tools/VERSION b/tools/VERSION
index 9524960..79b12b13 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 12
PATCH 0
PRERELEASE 5
-PRERELEASE_PATCH 8
+PRERELEASE_PATCH 9