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