/* * Use of this source code is governed by the ACE copyright license which * can be found in the LICENSE file in the third_party_mods/ace directory of * the source tree or at http://www1.cse.wustl.edu/~schmidt/ACE-copying.html. */ /* * This source code contain modifications to the original source code * which can be found here: * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html (section 3.2). * Modifications: * 1) Dynamic detection of native support for condition variables. * 2) Use of WebRTC defined types and classes. Renaming of some functions. * 3) Introduction of a second event for wake all functionality. This prevents * a thread from spinning on the same condition variable, preventing other * threads from waking up. */ // TODO (hellner): probably nicer to split up native and generic // implementation into two different files #include "condition_variable_windows.h" #include "critical_section_windows.h" #include "trace.h" namespace webrtc { bool ConditionVariableWindows::_winSupportConditionVariablesPrimitive = false; static HMODULE library = NULL; PInitializeConditionVariable _PInitializeConditionVariable; PSleepConditionVariableCS _PSleepConditionVariableCS; PWakeConditionVariable _PWakeConditionVariable; PWakeAllConditionVariable _PWakeAllConditionVariable; typedef void (WINAPI *PInitializeConditionVariable)(PCONDITION_VARIABLE); typedef BOOL (WINAPI *PSleepConditionVariableCS)(PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD); typedef void (WINAPI *PWakeConditionVariable)(PCONDITION_VARIABLE); typedef void (WINAPI *PWakeAllConditionVariable)(PCONDITION_VARIABLE); ConditionVariableWindows::ConditionVariableWindows() : _eventID(WAKEALL_0) { if (!library) { // Use native implementation if supported (i.e Vista+) library = LoadLibrary(TEXT("Kernel32.dll")); if (library) { WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Loaded Kernel.dll"); _PInitializeConditionVariable = (PInitializeConditionVariable) GetProcAddress( library, "InitializeConditionVariable"); _PSleepConditionVariableCS = (PSleepConditionVariableCS)GetProcAddress( library, "SleepConditionVariableCS"); _PWakeConditionVariable = (PWakeConditionVariable)GetProcAddress( library, "WakeConditionVariable"); _PWakeAllConditionVariable = (PWakeAllConditionVariable)GetProcAddress( library, "WakeAllConditionVariable"); if(_PInitializeConditionVariable && _PSleepConditionVariableCS && _PWakeConditionVariable && _PWakeAllConditionVariable) { WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Loaded native condition variables"); _winSupportConditionVariablesPrimitive = true; } } } if (_winSupportConditionVariablesPrimitive) { _PInitializeConditionVariable(&_conditionVariable); _events[WAKEALL_0] = NULL; _events[WAKEALL_1] = NULL; _events[WAKE] = NULL; } else { memset(&_numWaiters[0],0,sizeof(_numWaiters)); InitializeCriticalSection(&_numWaitersCritSect); _events[WAKEALL_0] = CreateEvent(NULL, // no security attributes TRUE, // manual-reset, sticky event FALSE, // initial state non-signaled NULL); // no name for event _events[WAKEALL_1] = CreateEvent(NULL, // no security attributes TRUE, // manual-reset, sticky event FALSE, // initial state non-signaled NULL); // no name for event _events[WAKE] = CreateEvent(NULL, // no security attributes FALSE, // auto-reset, sticky event FALSE, // initial state non-signaled NULL); // no name for event } } ConditionVariableWindows::~ConditionVariableWindows() { if(!_winSupportConditionVariablesPrimitive) { CloseHandle(_events[WAKE]); CloseHandle(_events[WAKEALL_1]); CloseHandle(_events[WAKEALL_0]); DeleteCriticalSection(&_numWaitersCritSect); } } void ConditionVariableWindows::SleepCS(CriticalSectionWrapper& critSect) { SleepCS(critSect, INFINITE); } bool ConditionVariableWindows::SleepCS(CriticalSectionWrapper& critSect, unsigned long maxTimeInMS) { CriticalSectionWindows* cs = reinterpret_cast( &critSect); if(_winSupportConditionVariablesPrimitive) { BOOL retVal = _PSleepConditionVariableCS(&_conditionVariable, &(cs->crit),maxTimeInMS); return (retVal == 0) ? false : true; }else { EnterCriticalSection(&_numWaitersCritSect); // Get the eventID for the event that will be triggered by next // WakeAll() call and start waiting for it. const EventWakeUpType eventID = (WAKEALL_0 == _eventID) ? WAKEALL_1 : WAKEALL_0; ++(_numWaiters[eventID]); LeaveCriticalSection(&_numWaitersCritSect); LeaveCriticalSection(&cs->crit); HANDLE events[2]; events[0] = _events[WAKE]; events[1] = _events[eventID]; const DWORD result = WaitForMultipleObjects(2, // Wait on 2 events. events, FALSE, // Wait for either. maxTimeInMS); const bool retVal = (result != WAIT_TIMEOUT); EnterCriticalSection(&_numWaitersCritSect); --(_numWaiters[eventID]); // Last waiter should only be true for WakeAll(). WakeAll() correspond // to position 1 in events[] -> (result == WAIT_OBJECT_0 + 1) const bool lastWaiter = (result == WAIT_OBJECT_0 + 1) && (_numWaiters[eventID] == 0); LeaveCriticalSection(&_numWaitersCritSect); if (lastWaiter) { // Reset/unset the WakeAll() event since all threads have been // released. ResetEvent(_events[eventID]); } EnterCriticalSection(&cs->crit); return retVal; } } void ConditionVariableWindows::Wake() { if(_winSupportConditionVariablesPrimitive) { _PWakeConditionVariable(&_conditionVariable); }else { EnterCriticalSection(&_numWaitersCritSect); const bool haveWaiters = (_numWaiters[WAKEALL_0] > 0) || (_numWaiters[WAKEALL_1] > 0); LeaveCriticalSection(&_numWaitersCritSect); if (haveWaiters) { SetEvent(_events[WAKE]); } } } void ConditionVariableWindows::WakeAll() { if(_winSupportConditionVariablesPrimitive) { _PWakeAllConditionVariable(&_conditionVariable); }else { EnterCriticalSection(&_numWaitersCritSect); // Update current WakeAll() event _eventID = (WAKEALL_0 == _eventID) ? WAKEALL_1 : WAKEALL_0; // Trigger current event const EventWakeUpType eventID = _eventID; const bool haveWaiters = _numWaiters[eventID] > 0; LeaveCriticalSection(&_numWaitersCritSect); if (haveWaiters) { SetEvent(_events[eventID]); } } } } // namespace webrtc