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