| 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 |