• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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