niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 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 | |
andrew@webrtc.org | 59ccd5c | 2011-12-15 00:17:43 +0000 | [diff] [blame] | 21 | #include "condition_variable_win.h" |
andrew@webrtc.org | 59ccd5c | 2011-12-15 00:17:43 +0000 | [diff] [blame] | 22 | #include "critical_section_win.h" |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 23 | #include "trace.h" |
| 24 | |
| 25 | namespace webrtc { |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 26 | |
| 27 | bool ConditionVariableWindows::win_support_condition_variables_primitive_ = |
| 28 | false; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 29 | static HMODULE library = NULL; |
| 30 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 31 | PInitializeConditionVariable PInitializeConditionVariable_; |
| 32 | PSleepConditionVariableCS PSleepConditionVariableCS_; |
| 33 | PWakeConditionVariable PWakeConditionVariable_; |
| 34 | PWakeAllConditionVariable PWakeAllConditionVariable_; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 35 | |
| 36 | typedef void (WINAPI *PInitializeConditionVariable)(PCONDITION_VARIABLE); |
| 37 | typedef BOOL (WINAPI *PSleepConditionVariableCS)(PCONDITION_VARIABLE, |
| 38 | PCRITICAL_SECTION, DWORD); |
| 39 | typedef void (WINAPI *PWakeConditionVariable)(PCONDITION_VARIABLE); |
| 40 | typedef void (WINAPI *PWakeAllConditionVariable)(PCONDITION_VARIABLE); |
| 41 | |
| 42 | ConditionVariableWindows::ConditionVariableWindows() |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 43 | : eventID_(WAKEALL_0) { |
| 44 | if (!library) { |
| 45 | // Use native implementation if supported (i.e Vista+) |
| 46 | library = LoadLibrary(TEXT("Kernel32.dll")); |
| 47 | if (library) { |
| 48 | WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Loaded Kernel.dll"); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 49 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 50 | PInitializeConditionVariable_ = |
| 51 | (PInitializeConditionVariable) GetProcAddress( |
| 52 | library, "InitializeConditionVariable"); |
| 53 | PSleepConditionVariableCS_ = (PSleepConditionVariableCS) GetProcAddress( |
| 54 | library, "SleepConditionVariableCS"); |
| 55 | PWakeConditionVariable_ = (PWakeConditionVariable) GetProcAddress( |
| 56 | library, "WakeConditionVariable"); |
| 57 | PWakeAllConditionVariable_ = (PWakeAllConditionVariable) GetProcAddress( |
| 58 | library, "WakeAllConditionVariable"); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 59 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 60 | if (PInitializeConditionVariable_ && PSleepConditionVariableCS_ |
| 61 | && PWakeConditionVariable_ && PWakeAllConditionVariable_) { |
| 62 | WEBRTC_TRACE( |
| 63 | kTraceStateInfo, kTraceUtility, -1, |
| 64 | "Loaded native condition variables"); |
| 65 | win_support_condition_variables_primitive_ = true; |
| 66 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 67 | } |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 68 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 69 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 70 | if (win_support_condition_variables_primitive_) { |
| 71 | PInitializeConditionVariable_(&condition_variable_); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 72 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 73 | events_[WAKEALL_0] = NULL; |
| 74 | events_[WAKEALL_1] = NULL; |
| 75 | events_[WAKE] = NULL; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 76 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 77 | } else { |
| 78 | memset(&num_waiters_[0], 0, sizeof(num_waiters_)); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 79 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 80 | InitializeCriticalSection(&num_waiters_crit_sect_); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 81 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 82 | events_[WAKEALL_0] = CreateEvent(NULL, // no security attributes |
| 83 | TRUE, // manual-reset, sticky event |
| 84 | FALSE, // initial state non-signaled |
| 85 | NULL); // no name for event |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 86 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 87 | events_[WAKEALL_1] = CreateEvent(NULL, // no security attributes |
| 88 | TRUE, // manual-reset, sticky event |
| 89 | FALSE, // initial state non-signaled |
| 90 | NULL); // no name for event |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 91 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 92 | events_[WAKE] = CreateEvent(NULL, // no security attributes |
| 93 | FALSE, // auto-reset, sticky event |
| 94 | FALSE, // initial state non-signaled |
| 95 | NULL); // no name for event |
| 96 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 97 | } |
| 98 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 99 | ConditionVariableWindows::~ConditionVariableWindows() { |
| 100 | if (!win_support_condition_variables_primitive_) { |
| 101 | CloseHandle(events_[WAKE]); |
| 102 | CloseHandle(events_[WAKEALL_1]); |
| 103 | CloseHandle(events_[WAKEALL_0]); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 104 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 105 | DeleteCriticalSection(&num_waiters_crit_sect_); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | void ConditionVariableWindows::SleepCS(CriticalSectionWrapper& crit_sect) { |
| 110 | SleepCS(crit_sect, INFINITE); |
| 111 | } |
| 112 | |
| 113 | bool ConditionVariableWindows::SleepCS(CriticalSectionWrapper& crit_sect, |
| 114 | unsigned long max_time_in_ms) { |
| 115 | CriticalSectionWindows* cs = |
| 116 | reinterpret_cast<CriticalSectionWindows*>(&crit_sect); |
| 117 | |
| 118 | if (win_support_condition_variables_primitive_) { |
| 119 | BOOL ret_val = PSleepConditionVariableCS_( |
| 120 | &condition_variable_, &(cs->crit), max_time_in_ms); |
| 121 | return (ret_val == 0) ? false : true; |
| 122 | } else { |
| 123 | EnterCriticalSection(&num_waiters_crit_sect_); |
| 124 | |
| 125 | // Get the eventID for the event that will be triggered by next |
| 126 | // WakeAll() call and start waiting for it. |
| 127 | const EventWakeUpType eventID = |
| 128 | (WAKEALL_0 == eventID_) ? WAKEALL_1 : WAKEALL_0; |
| 129 | |
| 130 | ++(num_waiters_[eventID]); |
| 131 | LeaveCriticalSection(&num_waiters_crit_sect_); |
| 132 | |
| 133 | LeaveCriticalSection(&cs->crit); |
| 134 | HANDLE events[2]; |
| 135 | events[0] = events_[WAKE]; |
| 136 | events[1] = events_[eventID]; |
| 137 | const DWORD result = WaitForMultipleObjects(2, // Wait on 2 events. |
| 138 | events, FALSE, // Wait for either. |
| 139 | max_time_in_ms); |
| 140 | |
| 141 | const bool ret_val = (result != WAIT_TIMEOUT); |
| 142 | |
| 143 | EnterCriticalSection(&num_waiters_crit_sect_); |
| 144 | --(num_waiters_[eventID]); |
| 145 | |
| 146 | // Last waiter should only be true for WakeAll(). WakeAll() correspond |
| 147 | // to position 1 in events[] -> (result == WAIT_OBJECT_0 + 1) |
| 148 | const bool last_waiter = (result == WAIT_OBJECT_0 + 1) |
| 149 | && (num_waiters_[eventID] == 0); |
| 150 | LeaveCriticalSection(&num_waiters_crit_sect_); |
| 151 | |
| 152 | if (last_waiter) { |
| 153 | // Reset/unset the WakeAll() event since all threads have been |
| 154 | // released. |
| 155 | ResetEvent(events_[eventID]); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 156 | } |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 157 | |
| 158 | EnterCriticalSection(&cs->crit); |
| 159 | return ret_val; |
| 160 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 161 | } |
| 162 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 163 | void ConditionVariableWindows::Wake() { |
| 164 | if (win_support_condition_variables_primitive_) { |
| 165 | PWakeConditionVariable_(&condition_variable_); |
| 166 | } else { |
| 167 | EnterCriticalSection(&num_waiters_crit_sect_); |
| 168 | const bool have_waiters = (num_waiters_[WAKEALL_0] > 0) |
| 169 | || (num_waiters_[WAKEALL_1] > 0); |
| 170 | LeaveCriticalSection(&num_waiters_crit_sect_); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 171 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 172 | if (have_waiters) { |
| 173 | SetEvent(events_[WAKE]); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 174 | } |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 175 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 176 | } |
| 177 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 178 | void ConditionVariableWindows::WakeAll() { |
| 179 | if (win_support_condition_variables_primitive_) { |
| 180 | PWakeAllConditionVariable_(&condition_variable_); |
| 181 | } else { |
| 182 | EnterCriticalSection(&num_waiters_crit_sect_); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 183 | |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 184 | // Update current WakeAll() event |
| 185 | eventID_ = (WAKEALL_0 == eventID_) ? WAKEALL_1 : WAKEALL_0; |
| 186 | |
| 187 | // Trigger current event |
| 188 | const EventWakeUpType eventID = eventID_; |
| 189 | const bool have_waiters = num_waiters_[eventID] > 0; |
| 190 | LeaveCriticalSection(&num_waiters_crit_sect_); |
| 191 | |
| 192 | if (have_waiters) { |
| 193 | SetEvent(events_[eventID]); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 194 | } |
phoglund@webrtc.org | a36d75a | 2012-11-14 09:55:04 +0000 | [diff] [blame] | 195 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 196 | } |
| 197 | |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 198 | } // namespace webrtc |