/*
* 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_win.h"
#include "critical_section_win.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<CriticalSectionWindows*>(
&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