Version 1.16.1

Cherry-pick 5f59a954f816ad0f46472eb4da05e714dbb7e117 to stable
Cherry-pick 18ec8dddd913398215747e7de3ecfb7f35c62e1c to stable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 27d3383..b9d7fae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 1.16.1 - 2016-05-24
+
+Patch release, resolves one issue:
+
+* VM: Fixes a bug that caused intermittent hangs on Windows.
+(SDK issue [26400](https://github.com/dart-lang/sdk/issues/26400))
+
 ## 1.16.0 - 2016-04-26
 
 ### Core library changes
diff --git a/runtime/bin/eventhandler_win.cc b/runtime/bin/eventhandler_win.cc
index dfd4985..f125bb8 100644
--- a/runtime/bin/eventhandler_win.cc
+++ b/runtime/bin/eventhandler_win.cc
@@ -126,6 +126,7 @@
       last_error_(NOERROR),
       flags_(0),
       read_thread_id_(Thread::kInvalidThreadId),
+      read_thread_handle_(NULL),
       read_thread_starting_(false),
       read_thread_finished_(false),
       monitor_(new Monitor()) {
@@ -188,8 +189,7 @@
 
 
 void Handle::WaitForReadThreadFinished() {
-  // Join the Reader thread if there is one.
-  ThreadId to_join = Thread::kInvalidThreadId;
+  HANDLE to_join = NULL;
   {
     MonitorLocker ml(monitor_);
     if (read_thread_id_ != Thread::kInvalidThreadId) {
@@ -197,12 +197,16 @@
         ml.Wait();
       }
       read_thread_finished_ = false;
-      to_join = read_thread_id_;
       read_thread_id_ = Thread::kInvalidThreadId;
+      to_join = read_thread_handle_;
+      read_thread_handle_ = NULL;
     }
   }
-  if (to_join != Thread::kInvalidThreadId) {
-    Thread::Join(to_join);
+  if (to_join != NULL) {
+    // Join the read thread.
+    DWORD res = WaitForSingleObject(to_join, INFINITE);
+    CloseHandle(to_join);
+    ASSERT(res == WAIT_OBJECT_0);
   }
 }
 
@@ -250,10 +254,12 @@
   ASSERT(read_thread_starting_);
   ASSERT(read_thread_id_ == Thread::kInvalidThreadId);
   read_thread_id_ = Thread::GetCurrentThreadId();
+  read_thread_handle_ = OpenThread(SYNCHRONIZE, false, read_thread_id_);
   read_thread_starting_ = false;
   ml.Notify();
 }
 
+
 void Handle::NotifyReadThreadFinished() {
   MonitorLocker ml(monitor_);
   ASSERT(!read_thread_finished_);
@@ -750,6 +756,7 @@
   MonitorLocker ml(monitor_);
   write_thread_running_ = true;
   thread_id_ = Thread::GetCurrentThreadId();
+  thread_handle_ = OpenThread(SYNCHRONIZE, false, thread_id_);
   // Notify we have started.
   ml.Notify();
 
@@ -839,7 +846,10 @@
     while (write_thread_exists_) {
       ml.Wait(Monitor::kNoTimeout);
     }
-    Thread::Join(thread_id_);
+    // Join the thread.
+    DWORD res = WaitForSingleObject(thread_handle_, INFINITE);
+    CloseHandle(thread_handle_);
+    ASSERT(res == WAIT_OBJECT_0);
   }
   Handle::DoClose();
 }
@@ -1366,6 +1376,7 @@
 EventHandlerImplementation::EventHandlerImplementation() {
   startup_monitor_ = new Monitor();
   handler_thread_id_ = Thread::kInvalidThreadId;
+  handler_thread_handle_ = NULL;
   completion_port_ =
       CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1);
   if (completion_port_ == NULL) {
@@ -1376,7 +1387,10 @@
 
 
 EventHandlerImplementation::~EventHandlerImplementation() {
-  Thread::Join(handler_thread_id_);
+  // Join the handler thread.
+  DWORD res = WaitForSingleObject(handler_thread_handle_, INFINITE);
+  CloseHandle(handler_thread_handle_);
+  ASSERT(res == WAIT_OBJECT_0);
   delete startup_monitor_;
   CloseHandle(completion_port_);
 }
@@ -1415,6 +1429,8 @@
   {
     MonitorLocker ml(handler_impl->startup_monitor_);
     handler_impl->handler_thread_id_ = Thread::GetCurrentThreadId();
+    handler_impl->handler_thread_handle_ =
+        OpenThread(SYNCHRONIZE, false, handler_impl->handler_thread_id_);
     ml.Notify();
   }
 
diff --git a/runtime/bin/eventhandler_win.h b/runtime/bin/eventhandler_win.h
index edb5c48..9daff93 100644
--- a/runtime/bin/eventhandler_win.h
+++ b/runtime/bin/eventhandler_win.h
@@ -263,6 +263,7 @@
   DWORD last_error_;
 
   ThreadId read_thread_id_;
+  HANDLE read_thread_handle_;
   bool read_thread_starting_;
   bool read_thread_finished_;
 
@@ -298,6 +299,7 @@
   explicit StdHandle(HANDLE handle)
       : FileHandle(handle),
         thread_id_(Thread::kInvalidThreadId),
+        thread_handle_(NULL),
         thread_wrote_(0),
         write_thread_exists_(false),
         write_thread_running_(false) {
@@ -312,6 +314,7 @@
 
  private:
   ThreadId thread_id_;
+  HANDLE thread_handle_;
   intptr_t thread_wrote_;
   bool write_thread_exists_;
   bool write_thread_running_;
@@ -538,6 +541,7 @@
 
   Monitor* startup_monitor_;
   ThreadId handler_thread_id_;
+  HANDLE handler_thread_handle_;
 
   TimeoutQueue timeout_queue_;  // Time for next timeout.
   bool shutdown_;
diff --git a/runtime/bin/thread.h b/runtime/bin/thread.h
index d95675d..979c77f 100644
--- a/runtime/bin/thread.h
+++ b/runtime/bin/thread.h
@@ -51,7 +51,6 @@
   static void SetThreadLocal(ThreadLocalKey key, uword value);
   static intptr_t GetMaxStackSize();
   static ThreadId GetCurrentThreadId();
-  static bool Join(ThreadId id);
   static intptr_t ThreadIdToIntPtr(ThreadId id);
   static bool Compare(ThreadId a, ThreadId b);
   static void GetThreadCpuUsage(ThreadId thread_id, int64_t* cpu_usage);
diff --git a/runtime/bin/thread_android.cc b/runtime/bin/thread_android.cc
index 4f5c547..ce27ea3 100644
--- a/runtime/bin/thread_android.cc
+++ b/runtime/bin/thread_android.cc
@@ -155,11 +155,6 @@
 }
 
 
-bool Thread::Join(ThreadId id) {
-  return false;
-}
-
-
 intptr_t Thread::ThreadIdToIntPtr(ThreadId id) {
   ASSERT(sizeof(id) == sizeof(intptr_t));
   return static_cast<intptr_t>(id);
diff --git a/runtime/bin/thread_linux.cc b/runtime/bin/thread_linux.cc
index 7a2a92a..6ea923e 100644
--- a/runtime/bin/thread_linux.cc
+++ b/runtime/bin/thread_linux.cc
@@ -156,11 +156,6 @@
 }
 
 
-bool Thread::Join(ThreadId id) {
-  return false;
-}
-
-
 intptr_t Thread::ThreadIdToIntPtr(ThreadId id) {
   ASSERT(sizeof(id) == sizeof(intptr_t));
   return static_cast<intptr_t>(id);
diff --git a/runtime/bin/thread_macos.cc b/runtime/bin/thread_macos.cc
index 8286835..9477e8ab 100644
--- a/runtime/bin/thread_macos.cc
+++ b/runtime/bin/thread_macos.cc
@@ -148,11 +148,6 @@
 }
 
 
-bool Thread::Join(ThreadId id) {
-  return false;
-}
-
-
 intptr_t Thread::ThreadIdToIntPtr(ThreadId id) {
   ASSERT(sizeof(id) == sizeof(intptr_t));
   return reinterpret_cast<intptr_t>(id);
diff --git a/runtime/bin/thread_win.cc b/runtime/bin/thread_win.cc
index db41196..92a9801 100644
--- a/runtime/bin/thread_win.cc
+++ b/runtime/bin/thread_win.cc
@@ -103,27 +103,6 @@
 }
 
 
-bool Thread::Join(ThreadId id) {
-  HANDLE handle = OpenThread(SYNCHRONIZE, false, id);
-
-  // TODO(zra): OSThread::Start() closes the handle to the thread. Thus, by the
-  // time we try to join the thread, its resources may have already been
-  // reclaimed, and joining will fail. This can be avoided in a couple of ways.
-  // First, GetCurrentThreadJoinId could call OpenThread and return a handle.
-  // This is bad, because each of those handles would have to be closed.
-  // Second OSThread could be refactored to no longer be AllStatic. Then the
-  // handle could be cached in the object by the Start method.
-  if (handle == NULL) {
-    return false;
-  }
-
-  DWORD res = WaitForSingleObject(handle, INFINITE);
-  CloseHandle(handle);
-  ASSERT(res == WAIT_OBJECT_0);
-  return true;
-}
-
-
 intptr_t Thread::ThreadIdToIntPtr(ThreadId id) {
   ASSERT(sizeof(id) <= sizeof(intptr_t));
   return static_cast<intptr_t>(id);
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 02c28db..aa77c40 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -9714,8 +9714,7 @@
   GlobalTimelineThreadData()
       : monitor_(new Monitor()),
         data_(new AppendData()),
-        running_(true),
-        join_id_(OSThread::kInvalidThreadJoinId) {
+        running_(true) {
   }
 
   ~GlobalTimelineThreadData() {
@@ -9733,16 +9732,13 @@
   AppendData* data() const { return data_; }
   uint8_t* buffer() const { return data_->buffer; }
   intptr_t buffer_length() const { return data_->buffer_length; }
-  ThreadJoinId join_id() const { return join_id_; }
 
   void set_running(bool running) { running_ = running; }
-  void set_join_id(ThreadJoinId join_id) { join_id_ = join_id; }
 
  private:
   Monitor* monitor_;
   AppendData* data_;
   bool running_;
-  ThreadJoinId join_id_;
 };
 
 
@@ -9758,7 +9754,6 @@
         Dart_GlobalTimelineGetTrace(AppendStreamConsumer, data->data());
     EXPECT(success);
     data->set_running(false);
-    data->set_join_id(OSThread::Current()->join_id());
     ml.Notify();
   }
 }
@@ -9805,7 +9800,6 @@
     }
     buffer = reinterpret_cast<char*>(data.buffer());
     buffer_length = data.buffer_length();
-    OSThread::Join(data.join_id());
   }
   EXPECT(buffer_length > 0);
   EXPECT(buffer != NULL);
@@ -9847,7 +9841,6 @@
     }
     buffer = reinterpret_cast<char*>(data2.buffer());
     buffer_length = data2.buffer_length();
-    OSThread::Join(data2.join_id());
   }
 
   EXPECT(buffer_length > 0);
diff --git a/runtime/vm/os_thread.cc b/runtime/vm/os_thread.cc
index 947b7f6..75a7a31 100644
--- a/runtime/vm/os_thread.cc
+++ b/runtime/vm/os_thread.cc
@@ -23,7 +23,9 @@
 OSThread::OSThread() :
     BaseThread(true),
     id_(OSThread::GetCurrentThreadId()),
-    join_id_(OSThread::GetCurrentThreadJoinId()),
+#if defined(DEBUG)
+    join_id_(kInvalidThreadJoinId),
+#endif
     trace_id_(OSThread::GetCurrentThreadTraceId()),
     name_(NULL),
     timeline_block_lock_(new Mutex()),
@@ -160,8 +162,8 @@
 }
 
 
-bool OSThread::IsThreadInList(ThreadJoinId join_id) {
-  if (join_id == OSThread::kInvalidThreadJoinId) {
+bool OSThread::IsThreadInList(ThreadId id) {
+  if (id == OSThread::kInvalidThreadId) {
     return false;
   }
   OSThreadIterator it;
@@ -169,8 +171,8 @@
     ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread());
     OSThread* t = it.Next();
     // An address test is not sufficient because the allocator may recycle
-    // the address for another Thread. Test against the thread's join id.
-    if (t->join_id() == join_id) {
+    // the address for another Thread. Test against the thread's id.
+    if (t->id() == id) {
       return true;
     }
   }
diff --git a/runtime/vm/os_thread.h b/runtime/vm/os_thread.h
index e0b41ac..19f340d 100644
--- a/runtime/vm/os_thread.h
+++ b/runtime/vm/os_thread.h
@@ -61,13 +61,8 @@
     return id_;
   }
 
-  ThreadJoinId join_id() const {
-    ASSERT(join_id_ != OSThread::kInvalidThreadJoinId);
-    return join_id_;
-  }
-
   ThreadId trace_id() const {
-    ASSERT(trace_id_ != OSThread::kInvalidThreadJoinId);
+    ASSERT(trace_id_ != OSThread::kInvalidThreadId);
     return trace_id_;
   }
 
@@ -175,10 +170,14 @@
   static bool Compare(ThreadId a, ThreadId b);
   static void GetThreadCpuUsage(ThreadId thread_id, int64_t* cpu_usage);
 
+  // This function can be called only once per OSThread, and should only be
+  // called when the retunred id will eventually be passed to OSThread::Join().
+  static ThreadJoinId GetCurrentThreadJoinId(OSThread* thread);
+
   // Called at VM startup and shutdown.
   static void InitOnce();
 
-  static bool IsThreadInList(ThreadJoinId join_id);
+  static bool IsThreadInList(ThreadId id);
 
   static void DisableOSThreadCreation();
   static void EnableOSThreadCreation();
@@ -206,7 +205,6 @@
 
   static void Cleanup();
   static ThreadId GetCurrentThreadTraceId();
-  static ThreadJoinId GetCurrentThreadJoinId();
   static OSThread* GetOSThreadFromThread(Thread* thread);
   static void AddThreadToListLocked(OSThread* thread);
   static void RemoveThreadFromList(OSThread* thread);
@@ -215,7 +213,11 @@
   static ThreadLocalKey thread_key_;
 
   const ThreadId id_;
-  const ThreadJoinId join_id_;
+#if defined(DEBUG)
+  // In DEBUG mode we use this field to ensure that GetCurrentThreadJoinId is
+  // only called once per OSThread.
+  ThreadJoinId join_id_;
+#endif
   const ThreadId trace_id_;  // Used to interface with tracing tools.
   char* name_;  // A name for this thread.
 
diff --git a/runtime/vm/os_thread_android.cc b/runtime/vm/os_thread_android.cc
index 9b93cbe..fab6d12 100644
--- a/runtime/vm/os_thread_android.cc
+++ b/runtime/vm/os_thread_android.cc
@@ -175,8 +175,17 @@
 }
 
 
-ThreadJoinId OSThread::GetCurrentThreadJoinId() {
-  return pthread_self();
+ThreadJoinId OSThread::GetCurrentThreadJoinId(OSThread* thread) {
+  ASSERT(thread != NULL);
+  // Make sure we're filling in the join id for the current thread.
+  ASSERT(thread->id() == GetCurrentThreadId());
+  // Make sure the join_id_ hasn't been set, yet.
+  DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId);
+  pthread_t id = pthread_self();
+#if defined(DEBUG)
+  thread->join_id_ = id;
+#endif
+  return id;
 }
 
 
diff --git a/runtime/vm/os_thread_linux.cc b/runtime/vm/os_thread_linux.cc
index a0e68f1..5bc8e8b 100644
--- a/runtime/vm/os_thread_linux.cc
+++ b/runtime/vm/os_thread_linux.cc
@@ -177,8 +177,17 @@
 }
 
 
-ThreadJoinId OSThread::GetCurrentThreadJoinId() {
-  return pthread_self();
+ThreadJoinId OSThread::GetCurrentThreadJoinId(OSThread* thread) {
+  ASSERT(thread != NULL);
+  // Make sure we're filling in the join id for the current thread.
+  ASSERT(thread->id() == GetCurrentThreadId());
+  // Make sure the join_id_ hasn't been set, yet.
+  DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId);
+  pthread_t id = pthread_self();
+#if defined(DEBUG)
+  thread->join_id_ = id;
+#endif
+  return id;
 }
 
 
diff --git a/runtime/vm/os_thread_macos.cc b/runtime/vm/os_thread_macos.cc
index 42dd360..20d1f2b 100644
--- a/runtime/vm/os_thread_macos.cc
+++ b/runtime/vm/os_thread_macos.cc
@@ -168,8 +168,17 @@
 }
 
 
-ThreadJoinId OSThread::GetCurrentThreadJoinId() {
-  return pthread_self();
+ThreadJoinId OSThread::GetCurrentThreadJoinId(OSThread* thread) {
+  ASSERT(thread != NULL);
+  // Make sure we're filling in the join id for the current thread.
+  ASSERT(thread->id() == GetCurrentThreadId());
+  // Make sure the join_id_ hasn't been set, yet.
+  DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId);
+  pthread_t id = pthread_self();
+#if defined(DEBUG)
+  thread->join_id_ = id;
+#endif
+  return id;
 }
 
 
diff --git a/runtime/vm/os_thread_win.cc b/runtime/vm/os_thread_win.cc
index 9d282bc..4cbef14 100644
--- a/runtime/vm/os_thread_win.cc
+++ b/runtime/vm/os_thread_win.cc
@@ -91,7 +91,7 @@
 
 
 const ThreadId OSThread::kInvalidThreadId = 0;
-const ThreadJoinId OSThread::kInvalidThreadJoinId = 0;
+const ThreadJoinId OSThread::kInvalidThreadJoinId = NULL;
 
 
 ThreadLocalKey OSThread::CreateThreadLocal(ThreadDestructor destructor) {
@@ -130,27 +130,25 @@
 }
 
 
-ThreadJoinId OSThread::GetCurrentThreadJoinId() {
-  // TODO(zra): Use the thread handle as the join id in order to have a more
-  // reliable join on windows.
-  return ::GetCurrentThreadId();
+ThreadJoinId OSThread::GetCurrentThreadJoinId(OSThread* thread) {
+  ASSERT(thread != NULL);
+  // Make sure we're filling in the join id for the current thread.
+  ThreadId id = GetCurrentThreadId();
+  ASSERT(thread->id() == id);
+  // Make sure the join_id_ hasn't been set, yet.
+  DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId);
+  HANDLE handle = OpenThread(SYNCHRONIZE, false, id);
+  ASSERT(handle != NULL);
+#if defined(DEBUG)
+  thread->join_id_ = handle;
+#endif
+  return handle;
 }
 
 
 void OSThread::Join(ThreadJoinId id) {
-  HANDLE handle = OpenThread(SYNCHRONIZE, false, id);
-
-  // TODO(zra): OSThread::Start() closes the handle to the thread. Thus, by the
-  // time we try to join the thread, its resources may have already been
-  // reclaimed, and joining will fail. This can be avoided in a couple of ways.
-  // First, GetCurrentThreadJoinId could call OpenThread and return a handle.
-  // This is bad, because each of those handles would have to be closed.
-  // Second OSThread could be refactored to no longer be AllStatic. Then the
-  // handle could be cached in the object by the Start method.
-  if (handle == NULL) {
-    return;
-  }
-
+  HANDLE handle = static_cast<HANDLE>(id);
+  ASSERT(handle != NULL);
   DWORD res = WaitForSingleObject(handle, INFINITE);
   CloseHandle(handle);
   ASSERT(res == WAIT_OBJECT_0);
diff --git a/runtime/vm/os_thread_win.h b/runtime/vm/os_thread_win.h
index 790306d..913b074 100644
--- a/runtime/vm/os_thread_win.h
+++ b/runtime/vm/os_thread_win.h
@@ -18,7 +18,7 @@
 
 typedef DWORD ThreadLocalKey;
 typedef DWORD ThreadId;
-typedef DWORD ThreadJoinId;
+typedef HANDLE ThreadJoinId;
 
 
 static const ThreadLocalKey kUnsetThreadLocalKey = TLS_OUT_OF_INDEXES;
diff --git a/runtime/vm/thread_interrupter.cc b/runtime/vm/thread_interrupter.cc
index 1e0d8cf..fa14d2e 100644
--- a/runtime/vm/thread_interrupter.cc
+++ b/runtime/vm/thread_interrupter.cc
@@ -157,7 +157,7 @@
     MonitorLocker startup_ml(monitor_);
     OSThread* os_thread = OSThread::Current();
     ASSERT(os_thread != NULL);
-    interrupter_thread_id_ = os_thread->join_id();
+    interrupter_thread_id_ = OSThread::GetCurrentThreadJoinId(os_thread);
     thread_running_ = true;
     startup_ml.Notify();
   }
diff --git a/runtime/vm/thread_pool.cc b/runtime/vm/thread_pool.cc
index fb0f7d7..0c326d3 100644
--- a/runtime/vm/thread_pool.cc
+++ b/runtime/vm/thread_pool.cc
@@ -212,6 +212,16 @@
 }
 
 
+void ThreadPool::SetIdleLocked(Worker* worker) {
+  ASSERT(mutex_.IsOwnedByCurrentThread());
+  ASSERT(worker->owned_ && !IsIdle(worker));
+  worker->idle_next_ = idle_workers_;
+  idle_workers_ = worker;
+  count_idle_++;
+  count_running_--;
+}
+
+
 void ThreadPool::SetIdleAndReapExited(Worker* worker) {
   JoinList* list = NULL;
   {
@@ -219,17 +229,25 @@
     if (shutting_down_) {
       return;
     }
-    ASSERT(worker->owned_ && !IsIdle(worker));
-    worker->idle_next_ = idle_workers_;
-    idle_workers_ = worker;
-    count_idle_++;
-    count_running_--;
-
-    // While we have the lock, opportunistically grab and clear the join_list_.
+    if (join_list_ == NULL) {
+      // Nothing to join, add to the idle list and return.
+      SetIdleLocked(worker);
+      return;
+    }
+    // There is something to join. Grab the join list, drop the lock, do the
+    // join, then grab the lock again and add to the idle list.
     list = join_list_;
     join_list_ = NULL;
   }
   JoinList::Join(&list);
+
+  {
+    MutexLocker ml(&mutex_);
+    if (shutting_down_) {
+      return;
+    }
+    SetIdleLocked(worker);
+  }
 }
 
 
@@ -250,7 +268,8 @@
   // so that we can join on it at the next opportunity.
   OSThread* os_thread = OSThread::Current();
   ASSERT(os_thread != NULL);
-  JoinList::AddLocked(os_thread->join_id(), &join_list_);
+  ThreadJoinId join_id = OSThread::GetCurrentThreadJoinId(os_thread);
+  JoinList::AddLocked(join_id, &join_list_);
   count_stopped_++;
   count_idle_--;
   return true;
@@ -430,7 +449,6 @@
   OSThread* os_thread = OSThread::Current();
   ASSERT(os_thread != NULL);
   ThreadId id = os_thread->id();
-  ThreadJoinId join_id = os_thread->join_id();
   ThreadPool* pool;
 
   // Set the thread's stack_base based on the current stack pointer.
@@ -454,6 +472,7 @@
     // Inform the thread pool that we are exiting. We remove this worker from
     // shutting_down_workers_ list because there will be no need for the
     // ThreadPool to take action for this worker.
+    ThreadJoinId join_id = OSThread::GetCurrentThreadJoinId(os_thread);
     {
       MutexLocker ml(&pool->mutex_);
       JoinList::AddLocked(join_id, &pool->join_list_);
diff --git a/runtime/vm/thread_pool.h b/runtime/vm/thread_pool.h
index 1713781..8cd29a2 100644
--- a/runtime/vm/thread_pool.h
+++ b/runtime/vm/thread_pool.h
@@ -125,6 +125,7 @@
   void ReapExitedIdleThreads();
 
   // Worker operations.
+  void SetIdleLocked(Worker* worker);  // Assumes mutex_ is held.
   void SetIdleAndReapExited(Worker* worker);
   bool ReleaseIdleWorker(Worker* worker);
 
diff --git a/runtime/vm/thread_test.cc b/runtime/vm/thread_test.cc
index aa006df..d0deba9 100644
--- a/runtime/vm/thread_test.cc
+++ b/runtime/vm/thread_test.cc
@@ -435,12 +435,13 @@
 
 VM_TEST_CASE(ThreadIterator_FindSelf) {
   OSThread* current = OSThread::Current();
-  EXPECT(OSThread::IsThreadInList(current->join_id()));
+  EXPECT(OSThread::IsThreadInList(current->id()));
 }
 
 
 struct ThreadIteratorTestParams {
-  ThreadId spawned_thread_join_id;
+  ThreadId spawned_thread_id;
+  ThreadJoinId spawned_thread_join_id;
   Monitor* monitor;
 };
 
@@ -452,9 +453,10 @@
   EXPECT(thread != NULL);
 
   MonitorLocker ml(params->monitor);
-  params->spawned_thread_join_id = thread->join_id();
-  EXPECT(params->spawned_thread_join_id != OSThread::kInvalidThreadJoinId);
-  EXPECT(OSThread::IsThreadInList(thread->join_id()));
+  params->spawned_thread_id = thread->id();
+  params->spawned_thread_join_id = OSThread::GetCurrentThreadJoinId(thread);
+  EXPECT(params->spawned_thread_id != OSThread::kInvalidThreadId);
+  EXPECT(OSThread::IsThreadInList(thread->id()));
   ml.Notify();
 }
 
@@ -463,25 +465,25 @@
 // on Windows. See |OnDartThreadExit| in os_thread_win.cc for more details.
 TEST_CASE(ThreadIterator_AddFindRemove) {
   ThreadIteratorTestParams params;
-  params.spawned_thread_join_id = OSThread::kInvalidThreadJoinId;
+  params.spawned_thread_id = OSThread::kInvalidThreadId;
   params.monitor = new Monitor();
 
   {
     MonitorLocker ml(params.monitor);
-    EXPECT(params.spawned_thread_join_id == OSThread::kInvalidThreadJoinId);
-    // Spawn thread and wait to receive the thread join id.
+    EXPECT(params.spawned_thread_id == OSThread::kInvalidThreadId);
+    // Spawn thread and wait to receive the thread id.
     OSThread::Start("ThreadIteratorTest",
                     ThreadIteratorTestMain,
                     reinterpret_cast<uword>(&params));
-    while (params.spawned_thread_join_id == OSThread::kInvalidThreadJoinId) {
+    while (params.spawned_thread_id == OSThread::kInvalidThreadId) {
       ml.Wait();
     }
+    EXPECT(params.spawned_thread_id != OSThread::kInvalidThreadId);
     EXPECT(params.spawned_thread_join_id != OSThread::kInvalidThreadJoinId);
-    // Join thread.
     OSThread::Join(params.spawned_thread_join_id);
   }
 
-  EXPECT(!OSThread::IsThreadInList(params.spawned_thread_join_id))
+  EXPECT(!OSThread::IsThreadInList(params.spawned_thread_id))
 
   delete params.monitor;
 }
diff --git a/tools/VERSION b/tools/VERSION
index 5f18786..43b3e89 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -26,6 +26,6 @@
 CHANNEL stable
 MAJOR 1
 MINOR 16
-PATCH 0
+PATCH 1
 PRERELEASE 0
 PRERELEASE_PATCH 0