summaryrefslogtreecommitdiffstats
path: root/js/src/threading/windows/ConditionVariable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/threading/windows/ConditionVariable.cpp')
-rw-r--r--js/src/threading/windows/ConditionVariable.cpp329
1 files changed, 7 insertions, 322 deletions
diff --git a/js/src/threading/windows/ConditionVariable.cpp b/js/src/threading/windows/ConditionVariable.cpp
index 868c35141..3c75a0f27 100644
--- a/js/src/threading/windows/ConditionVariable.cpp
+++ b/js/src/threading/windows/ConditionVariable.cpp
@@ -28,342 +28,34 @@
_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;
- };
+ CONDITION_VARIABLE cv_;
};
js::ConditionVariable::ConditionVariable()
{
- if (sNativeImports.supported())
- platformData()->native.initialize();
- else
- platformData()->fallback.initialize();
+ InitializeConditionVariable(&platformData()->cv_);
}
void
js::ConditionVariable::notify_one()
{
- if (sNativeImports.supported())
- platformData()->native.notify_one();
- else
- platformData()->fallback.notify_one();
+ WakeConditionVariable(&platformData()->cv_);
}
void
js::ConditionVariable::notify_all()
{
- if (sNativeImports.supported())
- platformData()->native.notify_all();
- else
- platformData()->fallback.notify_all();
+ WakeAllConditionVariable(&platformData()->cv_);
}
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);
+ bool r = SleepConditionVariableCS(&platformData()->cv_, cs, INFINITE);
MOZ_RELEASE_ASSERT(r);
}
@@ -390,11 +82,7 @@ js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock,
? INFINITE
: static_cast<DWORD>(msecd);
- BOOL r;
- if (sNativeImports.supported())
- r = platformData()->native.wait(cs, msec);
- else
- r = platformData()->fallback.wait(cs, msec);
+ BOOL r = SleepConditionVariableCS(&platformData()->cv_, cs, msec);
if (r)
return CVStatus::NoTimeout;
MOZ_RELEASE_ASSERT(GetLastError() == ERROR_TIMEOUT);
@@ -403,10 +91,7 @@ js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock,
js::ConditionVariable::~ConditionVariable()
{
- if (sNativeImports.supported())
- platformData()->native.destroy();
- else
- platformData()->fallback.destroy();
+ // Native condition variables don't require cleanup.
}
inline js::ConditionVariable::PlatformData*