1 // Copyright (C) 2014 The Android Open Source Project
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 #pragma once
16
17 #include "aemu/base/Compiler.h"
18 #include "aemu/base/synchronization/Lock.h"
19 #include "aemu/base/system/System.h"
20
21 #include <algorithm>
22 #include <inttypes.h>
23
24 #ifdef _WIN32
25 #include <windows.h>
26 #else
27 #include <pthread.h>
28 #endif
29
30 #include <assert.h>
31
32 namespace android {
33 namespace base {
34
35 // A class that implements a condition variable, which can be used in
36 // association with a Lock to blocking-wait for specific conditions.
37 // Useful to implement various synchronization data structures.
38 class ConditionVariable {
39 public:
40 // A set of functions to efficiently unlock the lock used with
41 // the current condition variable and signal or broadcast it.
42 //
43 // The functions are needed because on some platforms (Posix) it's more
44 // efficient to signal the variable before unlocking mutex, while on others
45 // (Windows) it's exactly the opposite. Functions implement the best way
46 // for each platform and abstract it out from the user.
47 void signalAndUnlock(StaticLock* lock) REQUIRES(lock) RELEASE(lock);
48 void signalAndUnlock(AutoLock* lock) REQUIRES(lock->mLock) RELEASE(lock->mLock);
49
50 void broadcastAndUnlock(StaticLock* lock) REQUIRES(lock) RELEASE(lock);
51 void broadcastAndUnlock(AutoLock* lock) REQUIRES(lock->mLock) RELEASE(lock->mLock);
52
wait(AutoLock * userLock)53 void wait(AutoLock* userLock) {
54 assert(userLock->mLocked);
55 wait(&userLock->mLock);
56 }
57
58 //
59 // Convenience functions to get rid of the loop in condition variable usage
60 // Instead of hand-writing a loop, e.g.
61 //
62 // while (mRefCount < 3) {
63 // mCv.wait(&mLock);
64 // }
65 //
66 // use the following two wait() overloads:
67 //
68 // mCv.wait(&mLock, [this]() { return mRefCount >= 3; });
69 //
70 // Parameters:
71 // |lock| - a Lock or AutoLock pointer used with the condition variable.
72 // |pred| - a functor predicate that's compatible with "bool pred()"
73 // signature and returns a condition when one should stop waiting.
74 //
75
76 template <class Predicate>
wait(StaticLock * lock,Predicate pred)77 void wait(StaticLock* lock, Predicate pred) {
78 while (!pred()) {
79 this->wait(lock);
80 }
81 }
82
83 template <class Predicate>
wait(AutoLock * lock,Predicate pred)84 void wait(AutoLock* lock, Predicate pred) {
85 this->wait(&lock->mLock, pred);
86 }
87
88 #ifdef _WIN32
89
ConditionVariable()90 ConditionVariable() {
91 ::InitializeConditionVariable(&mCond);
92 }
93
94 // There's no special function to destroy CONDITION_VARIABLE in Windows.
95 ~ConditionVariable() = default;
96
97 // Wait until the condition variable is signaled. Note that spurious
98 // wakeups are always a possibility, so always check the condition
99 // in a loop, i.e. do:
100 //
101 // while (!condition) { condVar.wait(&lock); }
102 //
103 // instead of:
104 //
105 // if (!condition) { condVar.wait(&lock); }
106 //
wait(StaticLock * userLock)107 void wait(StaticLock* userLock) {
108 ::SleepConditionVariableSRW(&mCond, &userLock->mLock, INFINITE, 0);
109 }
110
timedWait(StaticLock * userLock,uint64_t waitUntilUs)111 bool timedWait(StaticLock *userLock, uint64_t waitUntilUs) {
112 const auto now = android::base::getUnixTimeUs();
113 const auto timeout = waitUntilUs > now ?
114 std::max<uint32_t>(0, waitUntilUs - now) / 1000 : 0;
115 return ::SleepConditionVariableSRW(
116 &mCond, &userLock->mLock, timeout, 0) != 0;
117 }
118
119 // Signal that a condition was reached. This will wake at least (and
120 // preferrably) one waiting thread that is blocked on wait().
signal()121 void signal() {
122 ::WakeConditionVariable(&mCond);
123 }
124
125 // Like signal(), but wakes all of the waiting threads.
broadcast()126 void broadcast() {
127 ::WakeAllConditionVariable(&mCond);
128 }
129
130 private:
131 CONDITION_VARIABLE mCond;
132
133 #else // !_WIN32
134
135 // Note: on Posix systems, make it a naive wrapper around pthread_cond_t.
136
ConditionVariable()137 ConditionVariable() {
138 pthread_cond_init(&mCond, NULL);
139 }
140
~ConditionVariable()141 ~ConditionVariable() {
142 pthread_cond_destroy(&mCond);
143 }
144
wait(StaticLock * userLock)145 void wait(StaticLock* userLock) {
146 pthread_cond_wait(&mCond, &userLock->mLock);
147 }
148
timedWait(StaticLock * userLock,uint64_t waitUntilUs)149 bool timedWait(StaticLock* userLock, uint64_t waitUntilUs) {
150 timespec abstime;
151 abstime.tv_sec = waitUntilUs / 1000000LL;
152 abstime.tv_nsec = (waitUntilUs % 1000000LL) * 1000;
153 return pthread_cond_timedwait(&mCond, &userLock->mLock, &abstime) == 0;
154 }
155
signal()156 void signal() {
157 pthread_cond_signal(&mCond);
158 }
159
broadcast()160 void broadcast() {
161 pthread_cond_broadcast(&mCond);
162 }
163
164 private:
165 pthread_cond_t mCond;
166
167 #endif // !_WIN32
168
169 DISALLOW_COPY_ASSIGN_AND_MOVE(ConditionVariable);
170 };
171
172 #ifdef _WIN32
signalAndUnlock(StaticLock * lock)173 inline void ConditionVariable::signalAndUnlock(StaticLock* lock) {
174 lock->unlock();
175 signal();
176 }
signalAndUnlock(AutoLock * lock)177 inline void ConditionVariable::signalAndUnlock(AutoLock* lock) {
178 lock->unlock();
179 signal();
180 }
181
broadcastAndUnlock(StaticLock * lock)182 inline void ConditionVariable::broadcastAndUnlock(StaticLock* lock) {
183 lock->unlock();
184 broadcast();
185 }
broadcastAndUnlock(AutoLock * lock)186 inline void ConditionVariable::broadcastAndUnlock(AutoLock* lock) {
187 lock->unlock();
188 broadcast();
189 }
190 #else // !_WIN32
signalAndUnlock(StaticLock * lock)191 inline void ConditionVariable::signalAndUnlock(StaticLock* lock) REQUIRES(lock) RELEASE(lock) {
192 signal();
193 lock->unlock();
194 }
signalAndUnlock(AutoLock * lock)195 inline void ConditionVariable::signalAndUnlock(AutoLock* lock) REQUIRES(lock->mLock) RELEASE(lock->mLock) {
196 signal();
197 lock->unlock();
198 }
broadcastAndUnlock(StaticLock * lock)199 inline void ConditionVariable::broadcastAndUnlock(StaticLock* lock) REQUIRES(lock) RELEASE(lock) {
200 broadcast();
201 lock->unlock();
202 }
broadcastAndUnlock(AutoLock * lock)203 inline void ConditionVariable::broadcastAndUnlock(AutoLock* lock) REQUIRES(lock->mLock) RELEASE(lock->mLock) {
204 broadcast();
205 lock->unlock();
206 }
207 #endif // !_WIN32
208
209 } // namespace base
210 } // namespace android
211