summaryrefslogtreecommitdiffstats
path: root/js/src/threading/windows
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/threading/windows
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/threading/windows')
-rw-r--r--js/src/threading/windows/ConditionVariable.cpp418
-rw-r--r--js/src/threading/windows/MutexImpl.cpp85
-rw-r--r--js/src/threading/windows/MutexPlatformData.h19
-rw-r--r--js/src/threading/windows/Thread.cpp164
4 files changed, 686 insertions, 0 deletions
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';
+}