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