blob: 9a53308b14acd31f0be17db3c37765c592d07334 [file] [log] [blame]
Lingfeng Yangc02cb032020-10-26 14:21:25 -07001// 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 "base/Compiler.h"
18#include "base/Lock.h"
Lingfeng Yangc9feed72020-11-05 10:18:12 -080019#include "base/System.h"
20
21#include <algorithm>
22#include <inttypes.h>
Lingfeng Yangc02cb032020-10-26 14:21:25 -070023
24#ifdef _WIN32
25#include <windows.h>
26#else
27#include <pthread.h>
28#endif
29
30#include <assert.h>
31
32namespace android {
33namespace 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.
38class ConditionVariable {
39public:
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);
48 void signalAndUnlock(AutoLock* lock);
49
50 void broadcastAndUnlock(StaticLock* lock);
51 void broadcastAndUnlock(AutoLock* lock);
52
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>
77 void wait(StaticLock* lock, Predicate pred) {
78 while (!pred()) {
79 this->wait(lock);
80 }
81 }
82
83 template <class Predicate>
84 void wait(AutoLock* lock, Predicate pred) {
85 this->wait(&lock->mLock, pred);
86 }
87
88#ifdef _WIN32
89
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 //
107 void wait(StaticLock* userLock) {
108 ::SleepConditionVariableSRW(&mCond, &userLock->mLock, INFINITE, 0);
109 }
110
111 bool timedWait(StaticLock *userLock, uint64_t waitUntilUs) {
Lingfeng Yangc9feed72020-11-05 10:18:12 -0800112 const auto now = android::base::getUnixTimeUs();
Lingfeng Yangc02cb032020-10-26 14:21:25 -0700113 const auto timeout =
114 std::max<uint64_t>(0, waitUntilUs - now) / 1000;
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().
121 void signal() {
122 ::WakeConditionVariable(&mCond);
123 }
124
125 // Like signal(), but wakes all of the waiting threads.
126 void broadcast() {
127 ::WakeAllConditionVariable(&mCond);
128 }
129
130private:
131 CONDITION_VARIABLE mCond;
132
133#else // !_WIN32
134
135 // Note: on Posix systems, make it a naive wrapper around pthread_cond_t.
136
137 ConditionVariable() {
138 pthread_cond_init(&mCond, NULL);
139 }
140
141 ~ConditionVariable() {
142 pthread_cond_destroy(&mCond);
143 }
144
145 void wait(StaticLock* userLock) {
146 pthread_cond_wait(&mCond, &userLock->mLock);
147 }
148
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
156 void signal() {
157 pthread_cond_signal(&mCond);
158 }
159
160 void broadcast() {
161 pthread_cond_broadcast(&mCond);
162 }
163
164private:
165 pthread_cond_t mCond;
166
167#endif // !_WIN32
168
169 DISALLOW_COPY_ASSIGN_AND_MOVE(ConditionVariable);
170};
171
172#ifdef _WIN32
173inline void ConditionVariable::signalAndUnlock(StaticLock* lock) {
174 lock->unlock();
175 signal();
176}
177inline void ConditionVariable::signalAndUnlock(AutoLock* lock) {
178 lock->unlock();
179 signal();
180}
181
182inline void ConditionVariable::broadcastAndUnlock(StaticLock* lock) {
183 lock->unlock();
184 broadcast();
185}
186inline void ConditionVariable::broadcastAndUnlock(AutoLock* lock) {
187 lock->unlock();
188 broadcast();
189}
190#else // !_WIN32
191inline void ConditionVariable::signalAndUnlock(StaticLock* lock) {
192 signal();
193 lock->unlock();
194}
195inline void ConditionVariable::signalAndUnlock(AutoLock* lock) {
196 signal();
197 lock->unlock();
198}
199inline void ConditionVariable::broadcastAndUnlock(StaticLock* lock) {
200 broadcast();
201 lock->unlock();
202}
203inline void ConditionVariable::broadcastAndUnlock(AutoLock* lock) {
204 broadcast();
205 lock->unlock();
206}
207#endif // !_WIN32
208
209} // namespace base
210} // namespace android