225 lines
7.8 KiB
C++
225 lines
7.8 KiB
C++
|
/*
|
||
|
* 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<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
|