1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "common/libs/threads/cuttlefish_thread.h"
17
18 #include <android-base/logging.h>
19 #include "common/libs/threads/thunkers.h"
20 #include "common/libs/time/monotonic_time.h"
21
22 using cvd::ConditionVariable;
23 using cvd::Mutex;
24 using cvd::ScopedThread;
25 using cvd::time::MonotonicTimePoint;
26 using cvd::time::Milliseconds;
27
28 static const int FINISHED = 100;
29
SleepUntil(const MonotonicTimePoint & in)30 static void SleepUntil(const MonotonicTimePoint& in) {
31 struct timespec ts;
32 in.ToTimespec(&ts);
33 #ifdef CLOCK_MONOTONIC_RAW
34 // WARNING:
35 // While we do have CLOCK_MONOTONIC_RAW, we can't depend on it until:
36 // - ALL places relying on MonotonicTimePoint are fixed,
37 // - pthread supports pthread_timewait_monotonic.
38 // - CLOCK_MONOTONIC_RAW is re-enabled in monotonic_time.h.
39 //
40 // This is currently observable as a LEGITIMATE problem while running
41 // this test. DO NOT revert this to CLOCK_MONOTONIC_RAW until this is
42 // fixed everywhere AND this test passes.
43 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
44 #else
45 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
46 #endif
47 }
48
49 class MutexTest {
50 public:
MutexTest()51 MutexTest() : busy_(NULL), stage_(0) {}
52
Run()53 void Run() {
54 {
55 ScopedThread thread_a(cvd::thunk<void, &MutexTest::FastThread>, this);
56 ScopedThread thread_b(cvd::thunk<void, &MutexTest::SlowThread>, this);
57 }
58 LOG(INFO) << "MutexTest: completed at stage "
59 << stage_
60 << ", result: "
61 << ((stage_ == FINISHED) ? "PASSED" : "FAILED");
62 }
63
64 protected:
FastThread()65 void* FastThread() {
66 mutex_.Lock();
67 CHECK(busy_ == NULL);
68 busy_ = "FastThread";
69 SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
70 stage_ = 1;
71 busy_ = NULL;
72 mutex_.Unlock();
73 SleepUntil(MonotonicTimePoint::Now() + Milliseconds(10));
74 mutex_.Lock();
75 CHECK(busy_ == NULL);
76 busy_ = "FastThread";
77 CHECK(stage_ == 2);
78 stage_ = FINISHED;
79 busy_ = NULL;
80 mutex_.Unlock();
81 return NULL;
82 }
83
SlowThread()84 void* SlowThread() {
85 SleepUntil(MonotonicTimePoint::Now() + Milliseconds(50));
86 mutex_.Lock();
87 CHECK(busy_== NULL);
88 busy_ = "SlowThread";
89 CHECK(stage_ == 1);
90 SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
91 stage_ = 2;
92 busy_ = NULL;
93 mutex_.Unlock();
94 return NULL;
95 }
96
97 Mutex mutex_;
98 const char* busy_;
99 int stage_;
100 };
101
102 class NotifyOneTest {
103 public:
NotifyOneTest()104 NotifyOneTest() : cond_(&mutex_), signalled_(0) {}
105
Run()106 void Run() {
107 {
108 ScopedThread thread_s(
109 cvd::thunk<void, &NotifyOneTest::SignalThread>, this);
110 ScopedThread thread_w1(
111 cvd::thunk<void, &NotifyOneTest::WaitThread>, this);
112 ScopedThread thread_w2(
113 cvd::thunk<void, &NotifyOneTest::WaitThread>, this);
114 }
115 LOG(INFO) << "NotifyOneTest: completed, signalled "
116 << signalled_
117 << ", result: "
118 << ((signalled_ == 2) ? "PASSED" : "FAILED");
119 }
120
121 protected:
SignalThread()122 void* SignalThread() {
123 SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
124 mutex_.Lock();
125 cond_.NotifyOne();
126 mutex_.Unlock();
127 SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
128 mutex_.Lock();
129 CHECK(signalled_== 1);
130 cond_.NotifyOne();
131 mutex_.Unlock();
132 SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
133 mutex_.Lock();
134 CHECK(signalled_ == 2);
135 mutex_.Unlock();
136 return NULL;
137 }
138
WaitThread()139 void* WaitThread() {
140 mutex_.Lock();
141 cond_.Wait();
142 signalled_++;
143 mutex_.Unlock();
144 return NULL;
145 }
146
147 Mutex mutex_;
148 ConditionVariable cond_;
149 int signalled_;
150 };
151
152 class NotifyAllTest {
153 public:
NotifyAllTest()154 NotifyAllTest() : cond_(&mutex_), signalled_(0) {}
155
Run()156 void Run() {
157 {
158 ScopedThread thread_s(
159 cvd::thunk<void, &NotifyAllTest::SignalThread>, this);
160 ScopedThread thread_w1(
161 cvd::thunk<void, &NotifyAllTest::WaitThread>, this);
162 ScopedThread thread_w2(
163 cvd::thunk<void, &NotifyAllTest::WaitThread>, this);
164 }
165 printf("NotifyAllTest: completed, signalled %d (%s)\n",
166 signalled_, (signalled_ == 2) ? "PASSED" : "FAILED");
167 }
168
169 protected:
SignalThread()170 void* SignalThread() {
171 SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
172 mutex_.Lock();
173 cond_.NotifyAll();
174 mutex_.Unlock();
175 SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
176 mutex_.Lock();
177 CHECK(signalled_ == 2);
178 mutex_.Unlock();
179 return NULL;
180 }
181
WaitThread()182 void* WaitThread() {
183 mutex_.Lock();
184 cond_.Wait();
185 signalled_++;
186 mutex_.Unlock();
187 return NULL;
188 }
189
190 Mutex mutex_;
191 ConditionVariable cond_;
192 int signalled_;
193 };
194
195 class WaitUntilTest {
196 public:
WaitUntilTest()197 WaitUntilTest() : cond_(&mutex_), stage_(0) {}
198
Run()199 void Run() {
200 start_ = MonotonicTimePoint::Now();
201 {
202 ScopedThread thread_s(
203 cvd::thunk<void, &WaitUntilTest::SignalThread>, this);
204 ScopedThread thread_w2(
205 cvd::thunk<void, &WaitUntilTest::WaitThread>, this);
206 }
207 printf("WaitUntilTest: completed, stage %d (%s)\n",
208 stage_, (stage_ == FINISHED) ? "PASSED" : "FAILED");
209 }
210
211 protected:
SignalThread()212 void* SignalThread() {
213 SleepUntil(start_ + Milliseconds(200));
214 mutex_.Lock();
215 CHECK(stage_ == 2);
216 cond_.NotifyOne();
217 stage_ = 3;
218 mutex_.Unlock();
219 return NULL;
220 }
221
WaitThread()222 void* WaitThread() {
223 mutex_.Lock();
224 CHECK(stage_ == 0);
225 stage_ = 1;
226 cond_.WaitUntil(start_ + Milliseconds(50));
227 MonotonicTimePoint current(MonotonicTimePoint::Now());
228 CHECK(Milliseconds(current - start_).count() >= 50);
229 CHECK(Milliseconds(current - start_).count() <= 100);
230 stage_ = 2;
231 cond_.WaitUntil(start_ + Milliseconds(1000));
232 current = MonotonicTimePoint::Now();
233 CHECK(Milliseconds(current - start_).count() <= 500);
234 CHECK(stage_ == 3);
235 stage_ = FINISHED;
236 mutex_.Unlock();
237 return NULL;
238 }
239
240 Mutex mutex_;
241 ConditionVariable cond_;
242 int stage_;
243 MonotonicTimePoint start_;
244 };
245
main(int,char ** argv)246 int main(int, char**argv) {
247 ::android::base::InitLogging(argv, android::base::StderrLogger);
248 MutexTest mt;
249 mt.Run();
250 NotifyOneTest nt1;
251 nt1.Run();
252 NotifyAllTest nta;
253 nta.Run();
254 WaitUntilTest wu;
255 wu.Run();
256 }
257