From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001
From: "Matt A. Tobin" <mattatobin@localhost.localdomain>
Date: Fri, 2 Feb 2018 04:16:08 -0500
Subject: Add m-esr52 at 52.6.0

---
 js/src/threading/windows/ConditionVariable.cpp | 418 +++++++++++++++++++++++++
 js/src/threading/windows/MutexImpl.cpp         |  85 +++++
 js/src/threading/windows/MutexPlatformData.h   |  19 ++
 js/src/threading/windows/Thread.cpp            | 164 ++++++++++
 4 files changed, 686 insertions(+)
 create mode 100644 js/src/threading/windows/ConditionVariable.cpp
 create mode 100644 js/src/threading/windows/MutexImpl.cpp
 create mode 100644 js/src/threading/windows/MutexPlatformData.h
 create mode 100644 js/src/threading/windows/Thread.cpp

(limited to 'js/src/threading/windows')

diff --git a/js/src/threading/windows/ConditionVariable.cpp b/js/src/threading/windows/ConditionVariable.cpp
new file mode 100644
index 000000000..868c35141
--- /dev/null
+++ b/js/src/threading/windows/ConditionVariable.cpp
@@ -0,0 +1,418 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+
+#include <float.h>
+#include <intrin.h>
+#include <stdlib.h>
+#include <windows.h>
+
+#include "threading/ConditionVariable.h"
+#include "threading/Mutex.h"
+#include "threading/windows/MutexPlatformData.h"
+
+// Some versions of the Windows SDK have a bug where some interlocked functions
+// are not redefined as compiler intrinsics. Fix that for the interlocked
+// functions that are used in this file.
+#if defined(_MSC_VER) && !defined(InterlockedExchangeAdd)
+#define InterlockedExchangeAdd(addend, value)                                  \
+  _InterlockedExchangeAdd((volatile long*)(addend), (long)(value))
+#endif
+
+#if defined(_MSC_VER) && !defined(InterlockedIncrement)
+#define InterlockedIncrement(addend)                                           \
+  _InterlockedIncrement((volatile long*)(addend))
+#endif
+
+// Windows XP and Server 2003 don't support condition variables natively. The
+// NativeImports class is responsible for detecting native support and
+// retrieving the appropriate function pointers. It gets instantiated once,
+// using a static initializer.
+class ConditionVariableNativeImports
+{
+public:
+  ConditionVariableNativeImports() {
+    HMODULE kernel32_dll = GetModuleHandle("kernel32.dll");
+    MOZ_RELEASE_ASSERT(kernel32_dll != NULL);
+
+#define LOAD_SYMBOL(symbol) loadSymbol(kernel32_dll, #symbol, symbol)
+    supported_ = LOAD_SYMBOL(InitializeConditionVariable) &&
+                 LOAD_SYMBOL(WakeConditionVariable) &&
+                 LOAD_SYMBOL(WakeAllConditionVariable) &&
+                 LOAD_SYMBOL(SleepConditionVariableCS);
+#undef LOAD_SYMBOL
+  }
+
+  inline bool supported() const {
+    return supported_;
+  }
+
+  void(WINAPI* InitializeConditionVariable)(CONDITION_VARIABLE* ConditionVariable);
+  void(WINAPI* WakeAllConditionVariable)(PCONDITION_VARIABLE ConditionVariable);
+  void(WINAPI* WakeConditionVariable)(CONDITION_VARIABLE* ConditionVariable);
+  BOOL(WINAPI* SleepConditionVariableCS)(CONDITION_VARIABLE* ConditionVariable,
+                                         CRITICAL_SECTION* CriticalSection,
+                                         DWORD dwMilliseconds);
+
+private:
+  template <typename T>
+  inline bool loadSymbol(HMODULE module, const char* name, T& fn) {
+    FARPROC ptr = GetProcAddress(module, name);
+    if (!ptr)
+      return false;
+
+    fn = reinterpret_cast<T>(ptr);
+    return true;
+  }
+
+  bool supported_;
+};
+
+static ConditionVariableNativeImports sNativeImports;
+
+// Wrapper for native condition variable APIs.
+struct ConditionVariableNative
+{
+  inline void initialize() {
+    sNativeImports.InitializeConditionVariable(&cv_);
+  }
+
+  inline void destroy() {
+    // Native condition variables don't require cleanup.
+  }
+
+  inline void notify_one() { sNativeImports.WakeConditionVariable(&cv_); }
+
+  inline void notify_all() { sNativeImports.WakeAllConditionVariable(&cv_); }
+
+  inline bool wait(CRITICAL_SECTION* cs, DWORD msec) {
+    return sNativeImports.SleepConditionVariableCS(&cv_, cs, msec);
+  }
+
+private:
+  CONDITION_VARIABLE cv_;
+};
+
+// Fallback condition variable support for Windows XP and Server 2003. Given the
+// difficulty of testing on these antiquated platforms and their rapidly
+// diminishing market share, this implementation trades performance for
+// predictable behavior.
+struct ConditionVariableFallback
+{
+  static const uint32_t WAKEUP_MODE_NONE = 0;
+  static const uint32_t WAKEUP_MODE_ONE = 0x40000000;
+  static const uint32_t WAKEUP_MODE_ALL = 0x80000000;
+
+  static const uint32_t WAKEUP_MODE_MASK = WAKEUP_MODE_ONE | WAKEUP_MODE_ALL;
+  static const uint32_t SLEEPERS_COUNT_MASK = ~WAKEUP_MODE_MASK;
+
+  void initialize()
+  {
+    // Initialize the state variable to 0 sleepers, no wakeup.
+    sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
+
+    // Create a semaphore that prevents threads from entering sleep,
+    // or waking other threads while a wakeup is ongoing.
+    sleepWakeupSemaphore_ = CreateSemaphoreW(NULL, 1, 1, NULL);
+    MOZ_RELEASE_ASSERT(sleepWakeupSemaphore_);
+
+    // Use an auto-reset event for waking up a single sleeper.
+    wakeOneEvent_ = CreateEventW(NULL, FALSE, FALSE, NULL);
+    MOZ_RELEASE_ASSERT(wakeOneEvent_);
+
+    // Use a manual-reset event for waking up all sleepers.
+    wakeAllEvent_ = CreateEventW(NULL, TRUE, FALSE, NULL);
+    MOZ_RELEASE_ASSERT(wakeAllEvent_);
+  }
+
+  void destroy()
+  {
+    BOOL r;
+
+    MOZ_RELEASE_ASSERT(sleepersCountAndWakeupMode_ == (0 | WAKEUP_MODE_NONE));
+
+    r = CloseHandle(sleepWakeupSemaphore_);
+    MOZ_RELEASE_ASSERT(r);
+
+    r = CloseHandle(wakeOneEvent_);
+    MOZ_RELEASE_ASSERT(r);
+
+    r = CloseHandle(wakeAllEvent_);
+    MOZ_RELEASE_ASSERT(r);
+  }
+
+private:
+  void wakeup(uint32_t wakeupMode, HANDLE wakeEvent)
+  {
+    // Ensure that only one thread at a time can wake up others.
+    BOOL result = WaitForSingleObject(sleepWakeupSemaphore_, INFINITE);
+    MOZ_RELEASE_ASSERT(result == WAIT_OBJECT_0);
+
+    // Atomically set the wakeup mode and retrieve the number of sleepers.
+    uint32_t wcwm = InterlockedExchangeAdd(&sleepersCountAndWakeupMode_,
+                                           wakeupMode);
+    uint32_t sleepersCount = wcwm & SLEEPERS_COUNT_MASK;
+    MOZ_RELEASE_ASSERT((wcwm & WAKEUP_MODE_MASK) == WAKEUP_MODE_NONE);
+
+    if (sleepersCount > 0) {
+      // If there are any sleepers, set the wake event. The (last) woken
+      // up thread is responsible for releasing the semaphore.
+      BOOL success = SetEvent(wakeEvent);
+      MOZ_RELEASE_ASSERT(success);
+
+    } else {
+      // If there are no sleepers, set the wakeup mode back to 'none'
+      // and release the semaphore ourselves.
+      sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
+
+      BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL);
+      MOZ_RELEASE_ASSERT(success);
+    }
+  }
+
+public:
+  void notify_one() { wakeup(WAKEUP_MODE_ONE, wakeOneEvent_); }
+
+  void notify_all() { wakeup(WAKEUP_MODE_ALL, wakeAllEvent_); }
+
+  bool wait(CRITICAL_SECTION* userLock, DWORD msec)
+  {
+    // Make sure that we can't enter sleep when there are other threads
+    // that still need to wake up on either of the wake events being set.
+    DWORD result = WaitForSingleObject(sleepWakeupSemaphore_, INFINITE);
+    MOZ_RELEASE_ASSERT(result == WAIT_OBJECT_0);
+
+    // Register ourselves as a sleeper. Use an atomic operation, because
+    // if another thread times out at the same time, it will decrement the
+    // sleepers count without acquiring the semaphore.
+    uint32_t wcwm = InterlockedIncrement(&sleepersCountAndWakeupMode_);
+    MOZ_RELEASE_ASSERT((wcwm & WAKEUP_MODE_MASK) == WAKEUP_MODE_NONE);
+
+    // Now that that this thread has been enlisted as a sleeper, it is safe
+    // again for other threads to do a wakeup.
+    BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL);
+    MOZ_RELEASE_ASSERT(success);
+
+    // Release the caller's mutex.
+    LeaveCriticalSection(userLock);
+
+    // Wait for either event to become signaled, which happens when
+    // notify_one() or notify_all() is called, or for a timeout.
+    HANDLE handles[2] = { wakeOneEvent_, wakeAllEvent_ };
+    DWORD waitResult = WaitForMultipleObjects(2, handles, FALSE, msec);
+    MOZ_RELEASE_ASSERT(waitResult == WAIT_OBJECT_0 ||
+                       waitResult == WAIT_OBJECT_0 + 1 ||
+                       (waitResult == WAIT_TIMEOUT && msec != INFINITE));
+
+    // Atomically decrease the sleepers count and retrieve the wakeup mode
+    // and new sleepers count.
+    // If the wait returned because wakeOneEvent_ was set, we are certain
+    // that the wakeup mode will be WAKEUP_MODE_ONE. In that case,
+    // atomically reset the wakeup mode to 'none', because if another
+    // thread's sleep times out at same time and it finds that it was the
+    // last sleeper, it decides whether or not to reset the wakeOneEvent_
+    // based on the current wakeup mode.
+    uint32_t sub;
+    if (waitResult == WAIT_OBJECT_0)
+      sub = 1 | WAKEUP_MODE_ONE;
+    else
+      sub = 1;
+    // Note that InterlockedExchangeAdd returns the old value, but it's
+    // easier to work with the new value.
+    wcwm = InterlockedExchangeAdd(&sleepersCountAndWakeupMode_, -sub) - sub;
+
+    uint32_t wakeupMode = wcwm & WAKEUP_MODE_MASK;
+    uint32_t sleepersCount = wcwm & SLEEPERS_COUNT_MASK;
+
+    bool releaseSleepWakeupSemaphore = false;
+
+    if (waitResult == WAIT_OBJECT_0) {
+      // The wake-one event is an auto-reset event so if we're woken by
+      // it, it should already have been reset. We also already removed
+      // the WAKEUP_MODE_ONE bit so the wakeup mode should now be 'none'
+      // again.
+      MOZ_RELEASE_ASSERT(wakeupMode == WAKEUP_MODE_NONE);
+
+      // The signaling thread has acquired the enter-wakeup semaphore and
+      // expects the woken (this) thread to release it again.
+      releaseSleepWakeupSemaphore = true;
+
+    } else if (waitResult == WAIT_TIMEOUT && wakeupMode == WAKEUP_MODE_ONE &&
+               sleepersCount == 0) {
+      // In theory a race condition is possible where the last sleeper
+      // times out right at the moment that another thread signals it.
+      // If that just happened we now have a dangling signal event and
+      // mode, but no threads to be woken up by it, and we need to clean
+      // that up.
+      BOOL success = ResetEvent(wakeOneEvent_);
+      MOZ_RELEASE_ASSERT(success);
+
+      // This is safe - we are certain there are no other sleepers that
+      // could wake up right now, and the semaphore ensures that no
+      // non-sleeping threads are messing with
+      // sleepersCountAndWakeupMode_.
+      sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
+
+      // The signaling thread has acquired the sleep-wakeup semaphore and
+      // expects the woken thread to release it. But since there are no
+      // sleeping threads left this thread will do it instead.
+      releaseSleepWakeupSemaphore = true;
+
+    } else if (wakeupMode == WAKEUP_MODE_ALL && sleepersCount == 0) {
+      // If this was the last thread waking up in response to a
+      // notify_all, clear the wakeup mode and reset the wake-all event.
+      // A race condition similar to the case described above could
+      // occur, so waitResult could be WAIT_TIMEOUT, but that doesn't
+      // matter for the actions that need to be taken.
+      MOZ_RELEASE_ASSERT(waitResult = WAIT_OBJECT_0 + 1 ||
+                         waitResult == WAIT_TIMEOUT);
+
+      BOOL success = ResetEvent(wakeAllEvent_);
+      MOZ_RELEASE_ASSERT(success);
+
+      sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
+
+      // The broadcasting thread has acquired the enter-wakeup semaphore
+      // and expects the last thread that wakes up to release it.
+      releaseSleepWakeupSemaphore = true;
+
+    } else if ((waitResult == WAIT_TIMEOUT && msec != INFINITE) ||
+               (waitResult == WAIT_OBJECT_0 + 1 &&
+                wakeupMode == WAKEUP_MODE_ALL)) {
+      // Either:
+      //   * The wait timed out but found no active notify_one or notify_all
+      //     the moment it decreased the wait count.
+      //   * A notify_all woke up this thread but there are more threads
+      //     that need to be woken up by the wake-all event.
+      // These are ordinary conditions in which we don't have to do
+      // anything.
+
+    } else {
+      MOZ_CRASH("invalid wakeup condition");
+    }
+
+    // Release the enter-wakeup semaphore if the wakeup condition requires
+    // us to do it.
+    if (releaseSleepWakeupSemaphore) {
+      BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL);
+      MOZ_RELEASE_ASSERT(success);
+    }
+
+    // Reacquire the user mutex.
+    EnterCriticalSection(userLock);
+
+    // Return true if woken up, false when timed out.
+    if (waitResult == WAIT_TIMEOUT) {
+      SetLastError(ERROR_TIMEOUT);
+      return false;
+    }
+    return true;
+  }
+
+private:
+  uint32_t sleepersCountAndWakeupMode_;
+  HANDLE sleepWakeupSemaphore_;
+  HANDLE wakeOneEvent_;
+  HANDLE wakeAllEvent_;
+};
+
+struct js::ConditionVariable::PlatformData
+{
+  union
+  {
+    ConditionVariableNative native;
+    ConditionVariableFallback fallback;
+  };
+};
+
+js::ConditionVariable::ConditionVariable()
+{
+  if (sNativeImports.supported())
+    platformData()->native.initialize();
+  else
+    platformData()->fallback.initialize();
+}
+
+void
+js::ConditionVariable::notify_one()
+{
+  if (sNativeImports.supported())
+    platformData()->native.notify_one();
+  else
+    platformData()->fallback.notify_one();
+}
+
+void
+js::ConditionVariable::notify_all()
+{
+  if (sNativeImports.supported())
+    platformData()->native.notify_all();
+  else
+    platformData()->fallback.notify_all();
+}
+
+void
+js::ConditionVariable::wait(UniqueLock<Mutex>& lock)
+{
+  CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection;
+  bool r;
+  if (sNativeImports.supported())
+    r = platformData()->native.wait(cs, INFINITE);
+  else
+    r = platformData()->fallback.wait(cs, INFINITE);
+  MOZ_RELEASE_ASSERT(r);
+}
+
+js::CVStatus
+js::ConditionVariable::wait_until(UniqueLock<Mutex>& lock,
+                                  const mozilla::TimeStamp& abs_time)
+{
+  return wait_for(lock, abs_time - mozilla::TimeStamp::Now());
+}
+
+js::CVStatus
+js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock,
+                                const mozilla::TimeDuration& rel_time)
+{
+  CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection;
+
+  // Note that DWORD is unsigned, so we have to be careful to clamp at 0.
+  // If rel_time is Forever, then ToMilliseconds is +inf, which evaluates as
+  // greater than UINT32_MAX, resulting in the correct INFINITE wait.
+  double msecd = rel_time.ToMilliseconds();
+  DWORD msec = msecd < 0.0
+               ? 0
+               : msecd > UINT32_MAX
+                 ? INFINITE
+                 : static_cast<DWORD>(msecd);
+
+  BOOL r;
+  if (sNativeImports.supported())
+    r = platformData()->native.wait(cs, msec);
+  else
+    r = platformData()->fallback.wait(cs, msec);
+  if (r)
+    return CVStatus::NoTimeout;
+  MOZ_RELEASE_ASSERT(GetLastError() == ERROR_TIMEOUT);
+  return CVStatus::Timeout;
+}
+
+js::ConditionVariable::~ConditionVariable()
+{
+  if (sNativeImports.supported())
+    platformData()->native.destroy();
+  else
+    platformData()->fallback.destroy();
+}
+
+inline js::ConditionVariable::PlatformData*
+js::ConditionVariable::platformData()
+{
+  static_assert(sizeof platformData_ >= sizeof(PlatformData),
+                "platformData_ is too small");
+  return reinterpret_cast<PlatformData*>(platformData_);
+}
diff --git a/js/src/threading/windows/MutexImpl.cpp b/js/src/threading/windows/MutexImpl.cpp
new file mode 100644
index 000000000..385d1c8de
--- /dev/null
+++ b/js/src/threading/windows/MutexImpl.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DebugOnly.h"
+
+#include "jswin.h"
+
+#include "js/Utility.h"
+
+#include "threading/Mutex.h"
+#include "threading/windows/MutexPlatformData.h"
+
+namespace {
+
+// We build with a toolkit that supports WinXP, so we have to probe
+// for modern features at runtime. This is necessary because Vista and
+// later automatically allocate and subsequently leak a debug info
+// object for each critical section that we allocate unless we tell it
+// not to. In order to tell it not to, we need the extra flags field
+// provided by the Ex version of InitializeCriticalSection.
+struct MutexNativeImports
+{
+  using InitializeCriticalSectionExT = BOOL (WINAPI*)(CRITICAL_SECTION*, DWORD, DWORD);
+  InitializeCriticalSectionExT InitializeCriticalSectionEx;
+
+  MutexNativeImports() {
+    HMODULE kernel32_dll = GetModuleHandle("kernel32.dll");
+    MOZ_RELEASE_ASSERT(kernel32_dll != NULL);
+    InitializeCriticalSectionEx = reinterpret_cast<InitializeCriticalSectionExT>(
+      GetProcAddress(kernel32_dll, "InitializeCriticalSectionEx"));
+  }
+
+  bool hasInitializeCriticalSectionEx() const {
+    return InitializeCriticalSectionEx;
+  }
+};
+
+static MutexNativeImports NativeImports;
+
+} // (anonymous namespace)
+
+js::detail::MutexImpl::MutexImpl()
+{
+  AutoEnterOOMUnsafeRegion oom;
+  platformData_ = js_new<PlatformData>();
+  if (!platformData_)
+    oom.crash("js::Mutex::Mutex");
+
+  // This number was adopted from NSPR.
+  const static DWORD LockSpinCount = 1500;
+  BOOL r;
+  if (NativeImports.hasInitializeCriticalSectionEx()) {
+    r = NativeImports.InitializeCriticalSectionEx(&platformData()->criticalSection,
+                                                  LockSpinCount,
+                                                  CRITICAL_SECTION_NO_DEBUG_INFO);
+  } else {
+    r = InitializeCriticalSectionAndSpinCount(&platformData()->criticalSection,
+                                              LockSpinCount);
+  }
+  MOZ_RELEASE_ASSERT(r);
+}
+
+js::detail::MutexImpl::~MutexImpl()
+{
+  if (!platformData_)
+    return;
+
+  DeleteCriticalSection(&platformData()->criticalSection);
+  js_delete(platformData());
+}
+
+void
+js::detail::MutexImpl::lock()
+{
+  EnterCriticalSection(&platformData()->criticalSection);
+}
+
+void
+js::detail::MutexImpl::unlock()
+{
+  LeaveCriticalSection(&platformData()->criticalSection);
+}
diff --git a/js/src/threading/windows/MutexPlatformData.h b/js/src/threading/windows/MutexPlatformData.h
new file mode 100644
index 000000000..fbe7fc80d
--- /dev/null
+++ b/js/src/threading/windows/MutexPlatformData.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef platform_win_MutexPlatformData_h
+#define platform_win_MutexPlatformData_h
+
+#include "jswin.h"
+
+#include "threading/Mutex.h"
+
+struct js::detail::MutexImpl::PlatformData
+{
+  CRITICAL_SECTION criticalSection;
+};
+
+#endif // platform_win_MutexPlatformData_h
diff --git a/js/src/threading/windows/Thread.cpp b/js/src/threading/windows/Thread.cpp
new file mode 100644
index 000000000..29e8b16a1
--- /dev/null
+++ b/js/src/threading/windows/Thread.cpp
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <assert.h>
+#include <new.h>
+#include <process.h>
+
+#include <windows.h>
+
+#include "threading/Thread.h"
+
+class js::Thread::Id::PlatformData
+{
+  friend class js::Thread;
+  friend js::Thread::Id js::ThisThread::GetId();
+
+  HANDLE handle;
+  unsigned id;
+};
+
+/* static */ js::HashNumber
+js::Thread::Hasher::hash(const Lookup& l)
+{
+  return mozilla::HashBytes(l.platformData_, sizeof(l.platformData_));
+}
+
+inline js::Thread::Id::PlatformData*
+js::Thread::Id::platformData()
+{
+  static_assert(sizeof platformData_ >= sizeof(PlatformData),
+                "platformData_ is too small");
+  return reinterpret_cast<PlatformData*>(platformData_);
+}
+
+inline const js::Thread::Id::PlatformData*
+js::Thread::Id::platformData() const
+{
+  static_assert(sizeof platformData_ >= sizeof(PlatformData),
+                "platformData_ is too small");
+  return reinterpret_cast<const PlatformData*>(platformData_);
+}
+
+js::Thread::Id::Id()
+{
+  platformData()->handle = nullptr;
+  platformData()->id = 0;
+}
+
+bool
+js::Thread::Id::operator==(const Id& aOther) const
+{
+  return platformData()->id == aOther.platformData()->id;
+}
+
+js::Thread::Thread(Thread&& aOther)
+{
+  id_ = aOther.id_;
+  aOther.id_ = Id();
+}
+
+js::Thread&
+js::Thread::operator=(Thread&& aOther)
+{
+  MOZ_RELEASE_ASSERT(!joinable());
+  id_ = aOther.id_;
+  aOther.id_ = Id();
+  return *this;
+}
+
+bool
+js::Thread::create(unsigned int (__stdcall* aMain)(void*), void* aArg)
+{
+  // Use _beginthreadex and not CreateThread, because threads that are
+  // created with the latter leak a small amount of memory when they use
+  // certain msvcrt functions and then exit.
+  uintptr_t handle = _beginthreadex(nullptr, options_.stackSize(),
+                                    aMain, aArg,
+                                    STACK_SIZE_PARAM_IS_A_RESERVATION,
+                                    &id_.platformData()->id);
+  if (!handle) {
+    // The documentation does not say what state the thread id has if the method
+    // fails, so assume that it is undefined and reset it manually.
+    id_ = Id();
+    return false;
+  }
+  id_.platformData()->handle = reinterpret_cast<HANDLE>(handle);
+  return true;
+}
+
+void
+js::Thread::join()
+{
+  MOZ_RELEASE_ASSERT(joinable());
+  DWORD r = WaitForSingleObject(id_.platformData()->handle, INFINITE);
+  MOZ_RELEASE_ASSERT(r == WAIT_OBJECT_0);
+  BOOL success = CloseHandle(id_.platformData()->handle);
+  MOZ_RELEASE_ASSERT(success);
+  id_ = Id();
+}
+
+void
+js::Thread::detach()
+{
+  MOZ_RELEASE_ASSERT(joinable());
+  BOOL success = CloseHandle(id_.platformData()->handle);
+  MOZ_RELEASE_ASSERT(success);
+  id_ = Id();
+}
+
+js::Thread::Id
+js::ThisThread::GetId()
+{
+  js::Thread::Id id;
+  id.platformData()->handle = GetCurrentThread();
+  id.platformData()->id = GetCurrentThreadId();
+  MOZ_RELEASE_ASSERT(id != js::Thread::Id());
+  return id;
+}
+
+void
+js::ThisThread::SetName(const char* name)
+{
+  MOZ_RELEASE_ASSERT(name);
+
+#ifdef _MSC_VER
+  // Setting the thread name requires compiler support for structured
+  // exceptions, so this only works when compiled with MSVC.
+  static const DWORD THREAD_NAME_EXCEPTION = 0x406D1388;
+  static const DWORD THREAD_NAME_INFO_TYPE = 0x1000;
+
+#pragma pack(push, 8)
+  struct THREADNAME_INFO
+  {
+    DWORD dwType;
+    LPCSTR szName;
+    DWORD dwThreadID;
+    DWORD dwFlags;
+  };
+#pragma pack(pop)
+
+  THREADNAME_INFO info;
+  info.dwType = THREAD_NAME_INFO_TYPE;
+  info.szName = name;
+  info.dwThreadID = GetCurrentThreadId();
+  info.dwFlags = 0;
+
+  __try {
+    RaiseException(THREAD_NAME_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR),
+                   (ULONG_PTR*)&info);
+  } __except (EXCEPTION_EXECUTE_HANDLER) {
+    // Do nothing.
+  }
+#endif
+}
+
+void
+js::ThisThread::GetName(char* nameBuffer, size_t len)
+{
+  MOZ_RELEASE_ASSERT(len > 0);
+  *nameBuffer = '\0';
+}
-- 
cgit v1.2.3