win32_waiter.cc (4847B)
1 // Copyright 2023 The Abseil Authors. 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 // https://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 #include "absl/synchronization/internal/win32_waiter.h" 16 17 #ifdef ABSL_INTERNAL_HAVE_WIN32_WAITER 18 19 #include <windows.h> 20 21 #include "absl/base/config.h" 22 #include "absl/base/internal/raw_logging.h" 23 #include "absl/base/internal/thread_identity.h" 24 #include "absl/base/optimization.h" 25 #include "absl/synchronization/internal/kernel_timeout.h" 26 27 namespace absl { 28 ABSL_NAMESPACE_BEGIN 29 namespace synchronization_internal { 30 31 class Win32Waiter::WinHelper { 32 public: 33 static SRWLOCK *GetLock(Win32Waiter *w) { 34 return reinterpret_cast<SRWLOCK *>(&w->mu_storage_); 35 } 36 37 static CONDITION_VARIABLE *GetCond(Win32Waiter *w) { 38 return reinterpret_cast<CONDITION_VARIABLE *>(&w->cv_storage_); 39 } 40 41 static_assert(sizeof(SRWLOCK) == sizeof(void *), 42 "`mu_storage_` does not have the same size as SRWLOCK"); 43 static_assert(alignof(SRWLOCK) == alignof(void *), 44 "`mu_storage_` does not have the same alignment as SRWLOCK"); 45 46 static_assert(sizeof(CONDITION_VARIABLE) == sizeof(void *), 47 "`ABSL_CONDITION_VARIABLE_STORAGE` does not have the same size " 48 "as `CONDITION_VARIABLE`"); 49 static_assert( 50 alignof(CONDITION_VARIABLE) == alignof(void *), 51 "`cv_storage_` does not have the same alignment as `CONDITION_VARIABLE`"); 52 53 // The SRWLOCK and CONDITION_VARIABLE types must be trivially constructible 54 // and destructible because we never call their constructors or destructors. 55 static_assert(std::is_trivially_constructible<SRWLOCK>::value, 56 "The `SRWLOCK` type must be trivially constructible"); 57 static_assert( 58 std::is_trivially_constructible<CONDITION_VARIABLE>::value, 59 "The `CONDITION_VARIABLE` type must be trivially constructible"); 60 static_assert(std::is_trivially_destructible<SRWLOCK>::value, 61 "The `SRWLOCK` type must be trivially destructible"); 62 static_assert(std::is_trivially_destructible<CONDITION_VARIABLE>::value, 63 "The `CONDITION_VARIABLE` type must be trivially destructible"); 64 }; 65 66 class LockHolder { 67 public: 68 explicit LockHolder(SRWLOCK* mu) : mu_(mu) { 69 AcquireSRWLockExclusive(mu_); 70 } 71 72 LockHolder(const LockHolder&) = delete; 73 LockHolder& operator=(const LockHolder&) = delete; 74 75 ~LockHolder() { 76 ReleaseSRWLockExclusive(mu_); 77 } 78 79 private: 80 SRWLOCK* mu_; 81 }; 82 83 Win32Waiter::Win32Waiter() { 84 auto *mu = ::new (static_cast<void *>(&mu_storage_)) SRWLOCK; 85 auto *cv = ::new (static_cast<void *>(&cv_storage_)) CONDITION_VARIABLE; 86 InitializeSRWLock(mu); 87 InitializeConditionVariable(cv); 88 waiter_count_ = 0; 89 wakeup_count_ = 0; 90 } 91 92 bool Win32Waiter::Wait(KernelTimeout t) { 93 SRWLOCK *mu = WinHelper::GetLock(this); 94 CONDITION_VARIABLE *cv = WinHelper::GetCond(this); 95 96 LockHolder h(mu); 97 ++waiter_count_; 98 99 // Loop until we find a wakeup to consume or timeout. 100 // Note that, since the thread ticker is just reset, we don't need to check 101 // whether the thread is idle on the very first pass of the loop. 102 bool first_pass = true; 103 while (wakeup_count_ == 0) { 104 if (!first_pass) MaybeBecomeIdle(); 105 // No wakeups available, time to wait. 106 if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) { 107 // GetLastError() returns a Win32 DWORD, but we assign to 108 // unsigned long to simplify the ABSL_RAW_LOG case below. The uniform 109 // initialization guarantees this is not a narrowing conversion. 110 const unsigned long err{GetLastError()}; // NOLINT(runtime/int) 111 if (err == ERROR_TIMEOUT) { 112 --waiter_count_; 113 return false; 114 } else { 115 ABSL_RAW_LOG(FATAL, "SleepConditionVariableSRW failed: %lu", err); 116 } 117 } 118 first_pass = false; 119 } 120 // Consume a wakeup and we're done. 121 --wakeup_count_; 122 --waiter_count_; 123 return true; 124 } 125 126 void Win32Waiter::Post() { 127 LockHolder h(WinHelper::GetLock(this)); 128 ++wakeup_count_; 129 InternalCondVarPoke(); 130 } 131 132 void Win32Waiter::Poke() { 133 LockHolder h(WinHelper::GetLock(this)); 134 InternalCondVarPoke(); 135 } 136 137 void Win32Waiter::InternalCondVarPoke() { 138 if (waiter_count_ != 0) { 139 WakeConditionVariable(WinHelper::GetCond(this)); 140 } 141 } 142 143 } // namespace synchronization_internal 144 ABSL_NAMESPACE_END 145 } // namespace absl 146 147 #endif // ABSL_INTERNAL_HAVE_WIN32_WAITER