1/*
2 *  Use of this source code is governed by the ACE copyright license which
3 *  can be found in the LICENSE file in the third_party_mods/ace directory of
4 *  the source tree or at http://www1.cse.wustl.edu/~schmidt/ACE-copying.html.
5 */
6/*
7 *  This source code contain modifications to the original source code
8 *  which can be found here:
9 *  http://www.cs.wustl.edu/~schmidt/win32-cv-1.html (section 3.2).
10 *  Modifications:
11 *  1) Dynamic detection of native support for condition variables.
12 *  2) Use of WebRTC defined types and classes. Renaming of some functions.
13 *  3) Introduction of a second event for wake all functionality. This prevents
14 *     a thread from spinning on the same condition variable, preventing other
15 *     threads from waking up.
16 */
17
18// TODO (hellner): probably nicer to split up native and generic
19// implementation into two different files
20
21#include "condition_variable_win.h"
22
23#include "critical_section_win.h"
24#include "trace.h"
25
26namespace webrtc {
27bool ConditionVariableWindows::_winSupportConditionVariablesPrimitive = false;
28static HMODULE library = NULL;
29
30PInitializeConditionVariable  _PInitializeConditionVariable;
31PSleepConditionVariableCS     _PSleepConditionVariableCS;
32PWakeConditionVariable        _PWakeConditionVariable;
33PWakeAllConditionVariable     _PWakeAllConditionVariable;
34
35typedef void (WINAPI *PInitializeConditionVariable)(PCONDITION_VARIABLE);
36typedef BOOL (WINAPI *PSleepConditionVariableCS)(PCONDITION_VARIABLE,
37                                                 PCRITICAL_SECTION, DWORD);
38typedef void (WINAPI *PWakeConditionVariable)(PCONDITION_VARIABLE);
39typedef void (WINAPI *PWakeAllConditionVariable)(PCONDITION_VARIABLE);
40
41ConditionVariableWindows::ConditionVariableWindows()
42    : _eventID(WAKEALL_0)
43{
44    if (!library)
45    {
46        // Use native implementation if supported (i.e Vista+)
47        library = LoadLibrary(TEXT("Kernel32.dll"));
48        if (library)
49        {
50            WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
51                         "Loaded Kernel.dll");
52
53            _PInitializeConditionVariable =
54                (PInitializeConditionVariable) GetProcAddress(
55                    library,
56                    "InitializeConditionVariable");
57            _PSleepConditionVariableCS =
58                (PSleepConditionVariableCS)GetProcAddress(
59                    library,
60                    "SleepConditionVariableCS");
61            _PWakeConditionVariable =
62                (PWakeConditionVariable)GetProcAddress(
63                    library,
64                     "WakeConditionVariable");
65            _PWakeAllConditionVariable =
66                (PWakeAllConditionVariable)GetProcAddress(
67                    library,
68                    "WakeAllConditionVariable");
69
70            if(_PInitializeConditionVariable &&
71               _PSleepConditionVariableCS &&
72               _PWakeConditionVariable &&
73               _PWakeAllConditionVariable)
74            {
75                WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
76                             "Loaded native condition variables");
77                _winSupportConditionVariablesPrimitive = true;
78            }
79        }
80    }
81
82    if (_winSupportConditionVariablesPrimitive)
83    {
84        _PInitializeConditionVariable(&_conditionVariable);
85
86        _events[WAKEALL_0] = NULL;
87        _events[WAKEALL_1] = NULL;
88        _events[WAKE] = NULL;
89
90    } else {
91        memset(&_numWaiters[0],0,sizeof(_numWaiters));
92
93        InitializeCriticalSection(&_numWaitersCritSect);
94
95        _events[WAKEALL_0] = CreateEvent(NULL,  // no security attributes
96                                         TRUE,  // manual-reset, sticky event
97                                         FALSE, // initial state non-signaled
98                                         NULL); // no name for event
99
100        _events[WAKEALL_1] = CreateEvent(NULL,  // no security attributes
101                                         TRUE,  // manual-reset, sticky event
102                                         FALSE, // initial state non-signaled
103                                         NULL); // no name for event
104
105        _events[WAKE] = CreateEvent(NULL,  // no security attributes
106                                    FALSE, // auto-reset, sticky event
107                                    FALSE, // initial state non-signaled
108                                    NULL); // no name for event
109    }
110}
111
112ConditionVariableWindows::~ConditionVariableWindows()
113{
114    if(!_winSupportConditionVariablesPrimitive)
115    {
116        CloseHandle(_events[WAKE]);
117        CloseHandle(_events[WAKEALL_1]);
118        CloseHandle(_events[WAKEALL_0]);
119
120        DeleteCriticalSection(&_numWaitersCritSect);
121    }
122}
123
124void ConditionVariableWindows::SleepCS(CriticalSectionWrapper& critSect)
125{
126    SleepCS(critSect, INFINITE);
127}
128
129bool ConditionVariableWindows::SleepCS(CriticalSectionWrapper& critSect,
130                                       unsigned long maxTimeInMS)
131{
132    CriticalSectionWindows* cs = reinterpret_cast<CriticalSectionWindows*>(
133                                     &critSect);
134
135    if(_winSupportConditionVariablesPrimitive)
136    {
137        BOOL retVal = _PSleepConditionVariableCS(&_conditionVariable,
138                                                 &(cs->crit),maxTimeInMS);
139        return (retVal == 0) ? false : true;
140
141    }else
142    {
143        EnterCriticalSection(&_numWaitersCritSect);
144        // Get the eventID for the event that will be triggered by next
145        // WakeAll() call and start waiting for it.
146        const EventWakeUpType eventID = (WAKEALL_0 == _eventID) ?
147                                            WAKEALL_1 : WAKEALL_0;
148        ++(_numWaiters[eventID]);
149        LeaveCriticalSection(&_numWaitersCritSect);
150
151        LeaveCriticalSection(&cs->crit);
152        HANDLE events[2];
153        events[0] = _events[WAKE];
154        events[1] = _events[eventID];
155        const DWORD result = WaitForMultipleObjects(2, // Wait on 2 events.
156                                                    events,
157                                                    FALSE, // Wait for either.
158                                                    maxTimeInMS);
159
160        const bool retVal = (result != WAIT_TIMEOUT);
161
162        EnterCriticalSection(&_numWaitersCritSect);
163        --(_numWaiters[eventID]);
164        // Last waiter should only be true for WakeAll(). WakeAll() correspond
165        // to position 1 in events[] -> (result == WAIT_OBJECT_0 + 1)
166        const bool lastWaiter = (result == WAIT_OBJECT_0 + 1) &&
167                                (_numWaiters[eventID] == 0);
168        LeaveCriticalSection(&_numWaitersCritSect);
169
170        if (lastWaiter)
171        {
172            // Reset/unset the WakeAll() event since all threads have been
173            // released.
174            ResetEvent(_events[eventID]);
175        }
176
177        EnterCriticalSection(&cs->crit);
178        return retVal;
179    }
180}
181
182void
183ConditionVariableWindows::Wake()
184{
185    if(_winSupportConditionVariablesPrimitive)
186    {
187        _PWakeConditionVariable(&_conditionVariable);
188    }else
189    {
190        EnterCriticalSection(&_numWaitersCritSect);
191        const bool haveWaiters = (_numWaiters[WAKEALL_0] > 0) ||
192                                 (_numWaiters[WAKEALL_1] > 0);
193        LeaveCriticalSection(&_numWaitersCritSect);
194
195        if (haveWaiters)
196        {
197            SetEvent(_events[WAKE]);
198        }
199    }
200}
201
202void
203ConditionVariableWindows::WakeAll()
204{
205    if(_winSupportConditionVariablesPrimitive)
206    {
207        _PWakeAllConditionVariable(&_conditionVariable);
208    }else
209    {
210        EnterCriticalSection(&_numWaitersCritSect);
211        // Update current WakeAll() event
212        _eventID = (WAKEALL_0 == _eventID) ? WAKEALL_1 : WAKEALL_0;
213        // Trigger current event
214        const EventWakeUpType eventID = _eventID;
215        const bool haveWaiters = _numWaiters[eventID] > 0;
216        LeaveCriticalSection(&_numWaitersCritSect);
217
218        if (haveWaiters)
219        {
220            SetEvent(_events[eventID]);
221        }
222    }
223}
224} // namespace webrtc
225