OLD | NEW |
| (Empty) |
1 // Copyright 2004-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 // | |
16 // Defines methods of classes used to encapsulate | |
17 // the synchronization primitives. | |
18 | |
19 #include "omaha/base/synchronized.h" | |
20 | |
21 #include "omaha/base/debug.h" | |
22 #include "omaha/base/error.h" | |
23 #include "omaha/base/logging.h" | |
24 #include "omaha/base/system.h" | |
25 #include "omaha/base/system_info.h" | |
26 #include "omaha/base/user_info.h" | |
27 #include "omaha/base/utils.h" | |
28 | |
29 namespace omaha { | |
30 | |
31 typedef HANDLE WINAPI CreateMutexExFunction( | |
32 LPSECURITY_ATTRIBUTES attributes, | |
33 const WCHAR* name, | |
34 DWORD flags, | |
35 DWORD desired_access | |
36 ); | |
37 | |
38 #define CREATE_EVENT_MANUAL_RESET 0x01 | |
39 typedef HANDLE WINAPI CreateEventExFunction( | |
40 LPSECURITY_ATTRIBUTES attributes, | |
41 const WCHAR* name, | |
42 DWORD flags, | |
43 DWORD desired_access | |
44 ); | |
45 | |
46 CreateMutexExFunction* create_mutex_ex_function = NULL; | |
47 CreateEventExFunction* create_event_ex_function = NULL; | |
48 | |
49 void EnsureCreateEx() { | |
50 if ((create_mutex_ex_function && create_event_ex_function) || | |
51 !SystemInfo::IsRunningOnVistaOrLater()) { | |
52 return; | |
53 } | |
54 | |
55 HMODULE kernel32_module = ::GetModuleHandle(_T("kernel32.dll")); | |
56 if (!kernel32_module) { | |
57 ASSERT(kernel32_module, | |
58 (_T("[GetModuleHandle error 0x%x]"), ::GetLastError())); | |
59 return; | |
60 } | |
61 GPA(kernel32_module, "CreateMutexExW", &create_mutex_ex_function); | |
62 ASSERT(create_mutex_ex_function, | |
63 (_T("[GPA error 0x%x]"), ::GetLastError())); | |
64 | |
65 GPA(kernel32_module, "CreateEventExW", &create_event_ex_function); | |
66 ASSERT(create_event_ex_function, | |
67 (_T("[GPA error 0x%x]"), ::GetLastError())); | |
68 } | |
69 | |
70 HANDLE CreateMutexWithSyncAccess(const TCHAR* name, | |
71 LPSECURITY_ATTRIBUTES lock_attributes) { | |
72 EnsureCreateEx(); | |
73 if (create_mutex_ex_function) { | |
74 return create_mutex_ex_function(lock_attributes, name, 0, | |
75 SYNCHRONIZE | | |
76 MUTEX_MODIFY_STATE); // for ReleaseMutex | |
77 } | |
78 return ::CreateMutex(lock_attributes, false, name); | |
79 } | |
80 | |
81 HANDLE CreateEventWithSyncAccess(const TCHAR* name, | |
82 LPSECURITY_ATTRIBUTES event_attributes) { | |
83 EnsureCreateEx(); | |
84 if (create_event_ex_function) { | |
85 return create_event_ex_function(event_attributes, name, | |
86 CREATE_EVENT_MANUAL_RESET, | |
87 SYNCHRONIZE | | |
88 EVENT_MODIFY_STATE); // for Set/Reset, etc. | |
89 } | |
90 return ::CreateEvent(event_attributes, true, false, name); | |
91 } | |
92 | |
93 // c-tor will take mutex. | |
94 AutoSync::AutoSync(const Lockable *pLock) | |
95 : lock_(pLock), | |
96 first_time_(true) { | |
97 ASSERT(lock_, (L"")); | |
98 VERIFY(lock_->Lock(), (L"Failed to lock in constructor")); | |
99 } | |
100 | |
101 // c-tor will take mutex. | |
102 AutoSync::AutoSync(const Lockable &rLock) | |
103 : lock_(&rLock), | |
104 first_time_(true) { | |
105 ASSERT(lock_, (L"")); | |
106 VERIFY(lock_->Lock(), (L"Failed to lock in constructor")); | |
107 } | |
108 | |
109 // d-tor will release mutex. | |
110 AutoSync::~AutoSync() { | |
111 ASSERT(lock_, (L"")); | |
112 VERIFY(lock_->Unlock(), (L"Failed to unlock in denstructor")); | |
113 } | |
114 | |
115 // Allows to write the for loop of __mutexBlock macro | |
116 bool AutoSync::FirstTime() { | |
117 if (first_time_) { | |
118 first_time_ = false; | |
119 return true; | |
120 } | |
121 return false; | |
122 } | |
123 | |
124 // Constructor. | |
125 GLock::GLock() : mutex_(NULL) { | |
126 } | |
127 | |
128 bool GLock::InitializeWithSecAttr(const TCHAR* name, | |
129 LPSECURITY_ATTRIBUTES lock_attributes) { | |
130 ASSERT(!mutex_, (L"")); | |
131 | |
132 #if defined(DEBUG) || defined(ASSERT_IN_RELEASE) | |
133 name_ = name; | |
134 #endif | |
135 | |
136 mutex_ = CreateMutexWithSyncAccess(name, lock_attributes); | |
137 return mutex_ != NULL; | |
138 } | |
139 | |
140 // Create mutex return the status of creation. Sets to default DACL. | |
141 bool GLock::Initialize(const TCHAR* name) { | |
142 return InitializeWithSecAttr(name, NULL); | |
143 } | |
144 | |
145 // Clean up. | |
146 GLock::~GLock() { | |
147 if (mutex_) { | |
148 VERIFY(::CloseHandle(mutex_), (_T(""))); | |
149 } | |
150 }; | |
151 | |
152 // Wait until signaled. | |
153 bool GLock::Lock() const { | |
154 return Lock(INFINITE); | |
155 } | |
156 | |
157 bool GLock::Lock(DWORD dwMilliseconds) const { | |
158 ASSERT1(mutex_); | |
159 | |
160 DWORD ret = ::WaitForSingleObject(mutex_, dwMilliseconds); | |
161 if (ret == WAIT_OBJECT_0) { | |
162 return true; | |
163 } else if (ret == WAIT_ABANDONED) { | |
164 UTIL_LOG(LE, (_T("[GLock::Lock - mutex was abandoned %s]"), name_)); | |
165 return true; | |
166 } | |
167 return false; | |
168 } | |
169 | |
170 // Release. | |
171 bool GLock::Unlock() const { | |
172 ASSERT1(mutex_); | |
173 | |
174 bool ret = (false != ::ReleaseMutex(mutex_)); | |
175 ASSERT(ret, (_T("ReleaseMutex failed. Err=%i"), ::GetLastError())); | |
176 | |
177 return ret; | |
178 } | |
179 | |
180 LLock::LLock() { | |
181 InitializeCriticalSection(&critical_section_); | |
182 } | |
183 | |
184 LLock::~LLock() { | |
185 DeleteCriticalSection(&critical_section_); | |
186 } | |
187 | |
188 bool LLock::Lock() const { | |
189 EnterCriticalSection(&critical_section_); | |
190 return true; | |
191 } | |
192 | |
193 // not very precise funcion, but OK for our goals. | |
194 bool LLock::Lock(DWORD wait_ms) const { | |
195 if (::TryEnterCriticalSection(&critical_section_)) | |
196 return true; | |
197 DWORD ticks_at_the_begin_of_wait = GetTickCount(); | |
198 do { | |
199 ::Sleep(0); | |
200 if (::TryEnterCriticalSection(&critical_section_)) { | |
201 return true; | |
202 } | |
203 } while (::GetTickCount() - ticks_at_the_begin_of_wait < wait_ms); | |
204 return false; | |
205 } | |
206 | |
207 bool LLock::Unlock() const { | |
208 LeaveCriticalSection(&critical_section_); | |
209 return true; | |
210 } | |
211 | |
212 // LockCount is initialized to a value of -1; a value of 0 or greater indicates | |
213 // that the critical section is held or owned. When LockCount is not equal | |
214 // to -1, the OwningThread field contains the thread id that owns the section. | |
215 DWORD LLock::GetOwner() const { | |
216 return critical_section_.LockCount != -1 ? | |
217 reinterpret_cast<DWORD>(critical_section_.OwningThread) : 0; | |
218 } | |
219 | |
220 // Use this c-tor for interprocess gates. | |
221 Gate::Gate(const TCHAR * event_name) : gate_(NULL) { | |
222 VERIFY(Initialize(event_name), (_T(""))); | |
223 } | |
224 | |
225 // Use this c-tor for in-process gates. | |
226 Gate::Gate() : gate_(NULL) { | |
227 VERIFY(Initialize(NULL), (_T(""))); | |
228 } | |
229 | |
230 // clean up. | |
231 Gate::~Gate() { | |
232 VERIFY(CloseHandle(gate_), (_T(""))); | |
233 } | |
234 | |
235 bool Gate::Initialize(const TCHAR * event_name) { | |
236 // event_name may be NULL | |
237 ASSERT1(gate_ == NULL); | |
238 | |
239 // Create the event. The gate is initially closed. | |
240 // if this is in process gate we don't name event, otherwise we do. | |
241 // Created with default permissions. | |
242 gate_ = CreateEventWithSyncAccess(event_name, NULL); | |
243 return (NULL != gate_); | |
244 } | |
245 | |
246 // Open the gate. Anyone can go through. | |
247 bool Gate::Open() { | |
248 return FALSE != SetEvent(gate_); | |
249 } | |
250 | |
251 // Shut the gate closed. | |
252 bool Gate::Close() { | |
253 return FALSE != ResetEvent(gate_); | |
254 } | |
255 | |
256 bool Gate::Wait(DWORD msec) { | |
257 return WAIT_OBJECT_0 == WaitForSingleObject(gate_, msec); | |
258 } | |
259 | |
260 // Returns S_OK, and sets selected_gate to zero based index of the gate that | |
261 // was opened | |
262 // Returns E_FAIL if timeout occured or gate was abandoned. | |
263 HRESULT Gate::WaitAny(Gate const * const *gates, | |
264 int num_gates, | |
265 DWORD msec, | |
266 int *selected_gate) { | |
267 ASSERT1(selected_gate); | |
268 ASSERT1(gates); | |
269 | |
270 return WaitMultipleHelper(gates, num_gates, msec, selected_gate, false); | |
271 } | |
272 | |
273 // Returns S_OK if all gates were opened | |
274 // Returns E_FAIL if timeout occured or gate was abandoned. | |
275 HRESULT Gate::WaitAll(Gate const * const *gates, int num_gates, DWORD msec) { | |
276 ASSERT1(gates); | |
277 | |
278 return WaitMultipleHelper(gates, num_gates, msec, NULL, true); | |
279 } | |
280 | |
281 HRESULT Gate::WaitMultipleHelper(Gate const * const *gates, | |
282 int num_gates, | |
283 DWORD msec, | |
284 int *selected_gate, | |
285 bool wait_all) { | |
286 ASSERT1(gates); | |
287 ASSERT(num_gates > 0, (_T("There must be at least 1 gate"))); | |
288 | |
289 if ( num_gates <= 0 ) { | |
290 return E_FAIL; | |
291 } | |
292 HANDLE *gate_array = new HANDLE[ num_gates ]; | |
293 ASSERT1(gate_array); | |
294 for ( int i = 0 ; i < num_gates ; ++i ) { | |
295 gate_array[ i ] = gates[ i ]->gate_; | |
296 } | |
297 DWORD res = WaitForMultipleObjects(num_gates, | |
298 gate_array, | |
299 wait_all ? TRUE : FALSE, | |
300 msec); | |
301 delete[] gate_array; | |
302 | |
303 #pragma warning(disable : 4296) | |
304 // C4296: '>=' : expression is always true | |
305 if (WAIT_OBJECT_0 <= res && res < (WAIT_OBJECT_0 + num_gates)) { | |
306 if (selected_gate) { | |
307 *selected_gate = res - WAIT_OBJECT_0; | |
308 } | |
309 return S_OK; | |
310 } | |
311 #pragma warning(default : 4296) | |
312 return E_FAIL; | |
313 } | |
314 | |
315 bool WaitAllowRepaint(const Gate& gate, DWORD msec) { | |
316 DWORD wait = 0; | |
317 HANDLE gate_handle = gate; | |
318 while ((wait = ::MsgWaitForMultipleObjects(1, | |
319 &gate_handle, | |
320 FALSE, | |
321 msec, | |
322 QS_PAINT)) == WAIT_OBJECT_0 + 1) { | |
323 MSG msg; | |
324 if (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || | |
325 ::PeekMessage(&msg, NULL, WM_NCPAINT, WM_NCPAINT, PM_REMOVE)) { | |
326 ::TranslateMessage(&msg); | |
327 ::DispatchMessage(&msg); | |
328 } | |
329 } | |
330 return (wait == WAIT_OBJECT_0); | |
331 } | |
332 | |
333 // SimpleLock | |
334 // TODO(omaha): Replace InterlockedCompareExchange with | |
335 // InterlockedCompareExchangeAcquire | |
336 // and InterlockedDecrement with InterlockedDecrementRelease for Windows 2003 | |
337 | |
338 bool SimpleLock::Lock() const { | |
339 while (1 == ::InterlockedCompareExchange(&lock_, 1, 0)) | |
340 ::SleepEx(0, TRUE); | |
341 return true; | |
342 } | |
343 | |
344 bool SimpleLock::Unlock() const { | |
345 ::InterlockedDecrement(&lock_); | |
346 return true; | |
347 } | |
348 | |
349 // same with a delay in the loop to prevent CPU usage with significant | |
350 // contention | |
351 | |
352 bool SimpleLockWithDelay::Lock() const { | |
353 while (1 == ::InterlockedCompareExchange(&lock_, 1, 0)) | |
354 ::SleepEx(25, FALSE); | |
355 return true; | |
356 } | |
357 | |
358 bool SimpleLockWithDelay::Unlock() const { | |
359 ::InterlockedDecrement(&lock_); | |
360 return true; | |
361 } | |
362 | |
363 | |
364 CriticalSection::CriticalSection() | |
365 : number_entries_(0) { | |
366 InitializeCriticalSection(&critical_section_); | |
367 } | |
368 | |
369 // allow only one thread to hold a lock | |
370 CriticalSection::~CriticalSection() { | |
371 // we should not have to do anything in the destructor | |
372 ASSERT(!number_entries_, (_T("critical section destroyed while active"))); | |
373 while (number_entries_) { | |
374 LeaveCriticalSection(&critical_section_); | |
375 number_entries_--; | |
376 } | |
377 | |
378 DeleteCriticalSection(&critical_section_); | |
379 } | |
380 | |
381 // enter the critical section | |
382 // entries may be nested | |
383 void CriticalSection::Enter() { | |
384 EnterCriticalSection(&critical_section_); | |
385 number_entries_++; | |
386 } | |
387 | |
388 // exit the critical section | |
389 // number of exits must match number of entries | |
390 void CriticalSection::Exit() { | |
391 LeaveCriticalSection(&critical_section_); | |
392 number_entries_--; | |
393 } | |
394 | |
395 // Take a CriticalSection and lock it | |
396 SingleLock::SingleLock(CriticalSection * cs) { | |
397 ASSERT(cs, (L"")); | |
398 critical_section_ = cs; | |
399 critical_section_->Enter(); | |
400 } | |
401 | |
402 // If we haven't freed it yet, do so now since we fell out of scope | |
403 SingleLock::~SingleLock() { | |
404 if (critical_section_) { | |
405 critical_section_->Exit(); | |
406 critical_section_ = NULL; | |
407 } | |
408 } | |
409 | |
410 // Explicitly unlock | |
411 HRESULT SingleLock::Unlock() { | |
412 // If they did not | |
413 if (critical_section_ == NULL) | |
414 return S_FALSE; | |
415 | |
416 critical_section_->Exit(); | |
417 critical_section_ = NULL; | |
418 return S_OK; | |
419 } | |
420 | |
421 // Encapsulation for kernel Event. Initializes and destroys with it's lifetime | |
422 void EventObj::Init(const TCHAR * event_name) { | |
423 ASSERT(event_name, (L"")); | |
424 | |
425 h_ = ::CreateEvent(NULL, false, false, event_name); | |
426 ASSERT1(h_); | |
427 } | |
428 | |
429 EventObj::~EventObj() { | |
430 if (h_) { | |
431 VERIFY(CloseHandle(h_), (L"")); | |
432 h_ = NULL; | |
433 } | |
434 } | |
435 | |
436 BOOL EventObj::SetEvent() { | |
437 ASSERT(h_, (L"")); | |
438 return ::SetEvent(h_); | |
439 } | |
440 | |
441 // Is the given handle signaled? | |
442 // | |
443 // Typically used for events. | |
444 bool IsHandleSignaled(HANDLE h) { | |
445 ASSERT(h != NULL && | |
446 h != INVALID_HANDLE_VALUE, (_T(""))); | |
447 | |
448 DWORD result = ::WaitForSingleObject(h, 0); | |
449 if (result == WAIT_OBJECT_0) { | |
450 return true; | |
451 } | |
452 | |
453 ASSERT(result == WAIT_TIMEOUT, | |
454 (_T("unexpected result value: %u (hr=0x%x)"), | |
455 result, HRESULTFromLastError())); | |
456 return false; | |
457 } | |
458 | |
459 | |
460 // Create an id for the events/mutexes that can be used at the given scope. | |
461 // TODO(omaha): Error handling. | |
462 void CreateSyncId(const TCHAR* id, SyncScope scope, CString* sync_id) { | |
463 ASSERT1(id); | |
464 ASSERT1(sync_id); | |
465 | |
466 CString postfix; | |
467 switch (scope) { | |
468 default: | |
469 ASSERT1(false); | |
470 break; | |
471 | |
472 case SYNC_LOCAL: | |
473 sync_id->SetString(_T("Local\\")); | |
474 // no postfix for local ids | |
475 break; | |
476 | |
477 case SYNC_USER: | |
478 case SYNC_GLOBAL: | |
479 sync_id->SetString(_T("Global\\")); | |
480 | |
481 if (scope == SYNC_GLOBAL) { | |
482 // (MSDN insists that you can create objects with the same name with the | |
483 // prefixes "Global\" and "Local\" in a system "running Terminal | |
484 // Services". And it also assures that XP when running Fast User | |
485 // Switching uses Terminal Services. But when you try to create two | |
486 // objects with the same name but in the different namespaces on an | |
487 // XP Pro workstation NOT running Fast User Switching you can't - you | |
488 // get ERROR_ALREADY_EXISTS. And the reason is that in the Object table, | |
489 // Global and Local are both symlinks to the same object directory. | |
490 // Yet every technique that you can use to interrogate the system on | |
491 // whether or not the system is "running Terminal Services" says that | |
492 // the system is, in fact, running Terminal Services. | |
493 // Which is exactly what you'd expect, yet you can't create the | |
494 // two objects with the same name in different workspaces. So we change | |
495 // the name slightly.) | |
496 postfix.SetString(_T("_global")); | |
497 } else { | |
498 ASSERT1(scope == SYNC_USER); | |
499 // make the postfix the sid | |
500 VERIFY1(SUCCEEDED(omaha::user_info::GetProcessUser(NULL, | |
501 NULL, | |
502 &postfix))); | |
503 } | |
504 break; | |
505 } | |
506 | |
507 sync_id->Append(id); | |
508 sync_id->Append(postfix); | |
509 } | |
510 | |
511 } // namespace omaha | |
512 | |
OLD | NEW |