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
26 namespace webrtc {
27 bool ConditionVariableWindows::_winSupportConditionVariablesPrimitive = false;
28 static HMODULE library = NULL;
29
30 PInitializeConditionVariable _PInitializeConditionVariable;
31 PSleepConditionVariableCS _PSleepConditionVariableCS;
32 PWakeConditionVariable _PWakeConditionVariable;
33 PWakeAllConditionVariable _PWakeAllConditionVariable;
34
35 typedef void (WINAPI *PInitializeConditionVariable)(PCONDITION_VARIABLE);
36 typedef BOOL (WINAPI *PSleepConditionVariableCS)(PCONDITION_VARIABLE,
37 PCRITICAL_SECTION, DWORD);
38 typedef void (WINAPI *PWakeConditionVariable)(PCONDITION_VARIABLE);
39 typedef void (WINAPI *PWakeAllConditionVariable)(PCONDITION_VARIABLE);
40
ConditionVariableWindows()41 ConditionVariableWindows::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
~ConditionVariableWindows()112 ConditionVariableWindows::~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
SleepCS(CriticalSectionWrapper & critSect)124 void ConditionVariableWindows::SleepCS(CriticalSectionWrapper& critSect)
125 {
126 SleepCS(critSect, INFINITE);
127 }
128
SleepCS(CriticalSectionWrapper & critSect,unsigned long maxTimeInMS)129 bool 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
182 void
Wake()183 ConditionVariableWindows::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
202 void
WakeAll()203 ConditionVariableWindows::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