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