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