diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/threading/windows | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-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.cpp | 418 | ||||
-rw-r--r-- | js/src/threading/windows/MutexImpl.cpp | 85 | ||||
-rw-r--r-- | js/src/threading/windows/MutexPlatformData.h | 19 | ||||
-rw-r--r-- | js/src/threading/windows/Thread.cpp | 164 |
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'; +} |