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