1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/base/platform/condition-variable.h"
6
7 #include "src/base/platform/platform.h"
8 #include "src/base/platform/time.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10
11 namespace v8 {
12 namespace base {
13
TEST(ConditionVariable,WaitForAfterNofityOnSameThread)14 TEST(ConditionVariable, WaitForAfterNofityOnSameThread) {
15 for (int n = 0; n < 10; ++n) {
16 Mutex mutex;
17 ConditionVariable cv;
18
19 LockGuard<Mutex> lock_guard(&mutex);
20
21 cv.NotifyOne();
22 EXPECT_FALSE(cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
23
24 cv.NotifyAll();
25 EXPECT_FALSE(cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
26 }
27 }
28
29
30 namespace {
31
32 class ThreadWithMutexAndConditionVariable final : public Thread {
33 public:
ThreadWithMutexAndConditionVariable()34 ThreadWithMutexAndConditionVariable()
35 : Thread(Options("ThreadWithMutexAndConditionVariable")),
36 running_(false),
37 finished_(false) {}
38
Run()39 void Run() override {
40 LockGuard<Mutex> lock_guard(&mutex_);
41 running_ = true;
42 cv_.NotifyOne();
43 while (running_) {
44 cv_.Wait(&mutex_);
45 }
46 finished_ = true;
47 cv_.NotifyAll();
48 }
49
50 bool running_;
51 bool finished_;
52 ConditionVariable cv_;
53 Mutex mutex_;
54 };
55
56 } // namespace
57
58
TEST(ConditionVariable,MultipleThreadsWithSeparateConditionVariables)59 TEST(ConditionVariable, MultipleThreadsWithSeparateConditionVariables) {
60 static const int kThreadCount = 128;
61 ThreadWithMutexAndConditionVariable threads[kThreadCount];
62
63 for (int n = 0; n < kThreadCount; ++n) {
64 LockGuard<Mutex> lock_guard(&threads[n].mutex_);
65 EXPECT_FALSE(threads[n].running_);
66 EXPECT_FALSE(threads[n].finished_);
67 threads[n].Start();
68 // Wait for nth thread to start.
69 while (!threads[n].running_) {
70 threads[n].cv_.Wait(&threads[n].mutex_);
71 }
72 }
73
74 for (int n = kThreadCount - 1; n >= 0; --n) {
75 LockGuard<Mutex> lock_guard(&threads[n].mutex_);
76 EXPECT_TRUE(threads[n].running_);
77 EXPECT_FALSE(threads[n].finished_);
78 }
79
80 for (int n = 0; n < kThreadCount; ++n) {
81 LockGuard<Mutex> lock_guard(&threads[n].mutex_);
82 EXPECT_TRUE(threads[n].running_);
83 EXPECT_FALSE(threads[n].finished_);
84 // Tell the nth thread to quit.
85 threads[n].running_ = false;
86 threads[n].cv_.NotifyOne();
87 }
88
89 for (int n = kThreadCount - 1; n >= 0; --n) {
90 // Wait for nth thread to quit.
91 LockGuard<Mutex> lock_guard(&threads[n].mutex_);
92 while (!threads[n].finished_) {
93 threads[n].cv_.Wait(&threads[n].mutex_);
94 }
95 EXPECT_FALSE(threads[n].running_);
96 EXPECT_TRUE(threads[n].finished_);
97 }
98
99 for (int n = 0; n < kThreadCount; ++n) {
100 threads[n].Join();
101 LockGuard<Mutex> lock_guard(&threads[n].mutex_);
102 EXPECT_FALSE(threads[n].running_);
103 EXPECT_TRUE(threads[n].finished_);
104 }
105 }
106
107
108 namespace {
109
110 class ThreadWithSharedMutexAndConditionVariable final : public Thread {
111 public:
ThreadWithSharedMutexAndConditionVariable()112 ThreadWithSharedMutexAndConditionVariable()
113 : Thread(Options("ThreadWithSharedMutexAndConditionVariable")),
114 running_(false),
115 finished_(false),
116 cv_(NULL),
117 mutex_(NULL) {}
118
Run()119 void Run() override {
120 LockGuard<Mutex> lock_guard(mutex_);
121 running_ = true;
122 cv_->NotifyAll();
123 while (running_) {
124 cv_->Wait(mutex_);
125 }
126 finished_ = true;
127 cv_->NotifyAll();
128 }
129
130 bool running_;
131 bool finished_;
132 ConditionVariable* cv_;
133 Mutex* mutex_;
134 };
135
136 } // namespace
137
138
TEST(ConditionVariable,MultipleThreadsWithSharedSeparateConditionVariables)139 TEST(ConditionVariable, MultipleThreadsWithSharedSeparateConditionVariables) {
140 static const int kThreadCount = 128;
141 ThreadWithSharedMutexAndConditionVariable threads[kThreadCount];
142 ConditionVariable cv;
143 Mutex mutex;
144
145 for (int n = 0; n < kThreadCount; ++n) {
146 threads[n].mutex_ = &mutex;
147 threads[n].cv_ = &cv;
148 }
149
150 // Start all threads.
151 {
152 LockGuard<Mutex> lock_guard(&mutex);
153 for (int n = 0; n < kThreadCount; ++n) {
154 EXPECT_FALSE(threads[n].running_);
155 EXPECT_FALSE(threads[n].finished_);
156 threads[n].Start();
157 }
158 }
159
160 // Wait for all threads to start.
161 {
162 LockGuard<Mutex> lock_guard(&mutex);
163 for (int n = kThreadCount - 1; n >= 0; --n) {
164 while (!threads[n].running_) {
165 cv.Wait(&mutex);
166 }
167 }
168 }
169
170 // Make sure that all threads are running.
171 {
172 LockGuard<Mutex> lock_guard(&mutex);
173 for (int n = 0; n < kThreadCount; ++n) {
174 EXPECT_TRUE(threads[n].running_);
175 EXPECT_FALSE(threads[n].finished_);
176 }
177 }
178
179 // Tell all threads to quit.
180 {
181 LockGuard<Mutex> lock_guard(&mutex);
182 for (int n = kThreadCount - 1; n >= 0; --n) {
183 EXPECT_TRUE(threads[n].running_);
184 EXPECT_FALSE(threads[n].finished_);
185 // Tell the nth thread to quit.
186 threads[n].running_ = false;
187 }
188 cv.NotifyAll();
189 }
190
191 // Wait for all threads to quit.
192 {
193 LockGuard<Mutex> lock_guard(&mutex);
194 for (int n = 0; n < kThreadCount; ++n) {
195 while (!threads[n].finished_) {
196 cv.Wait(&mutex);
197 }
198 }
199 }
200
201 // Make sure all threads are finished.
202 {
203 LockGuard<Mutex> lock_guard(&mutex);
204 for (int n = kThreadCount - 1; n >= 0; --n) {
205 EXPECT_FALSE(threads[n].running_);
206 EXPECT_TRUE(threads[n].finished_);
207 }
208 }
209
210 // Join all threads.
211 for (int n = 0; n < kThreadCount; ++n) {
212 threads[n].Join();
213 }
214 }
215
216
217 namespace {
218
219 class LoopIncrementThread final : public Thread {
220 public:
LoopIncrementThread(int rem,int * counter,int limit,int thread_count,ConditionVariable * cv,Mutex * mutex)221 LoopIncrementThread(int rem, int* counter, int limit, int thread_count,
222 ConditionVariable* cv, Mutex* mutex)
223 : Thread(Options("LoopIncrementThread")),
224 rem_(rem),
225 counter_(counter),
226 limit_(limit),
227 thread_count_(thread_count),
228 cv_(cv),
229 mutex_(mutex) {
230 EXPECT_LT(rem, thread_count);
231 EXPECT_EQ(0, limit % thread_count);
232 }
233
Run()234 void Run() override {
235 int last_count = -1;
236 while (true) {
237 LockGuard<Mutex> lock_guard(mutex_);
238 int count = *counter_;
239 while (count % thread_count_ != rem_ && count < limit_) {
240 cv_->Wait(mutex_);
241 count = *counter_;
242 }
243 if (count >= limit_) break;
244 EXPECT_EQ(*counter_, count);
245 if (last_count != -1) {
246 EXPECT_EQ(last_count + (thread_count_ - 1), count);
247 }
248 count++;
249 *counter_ = count;
250 last_count = count;
251 cv_->NotifyAll();
252 }
253 }
254
255 private:
256 const int rem_;
257 int* counter_;
258 const int limit_;
259 const int thread_count_;
260 ConditionVariable* cv_;
261 Mutex* mutex_;
262 };
263
264 } // namespace
265
266
TEST(ConditionVariable,LoopIncrement)267 TEST(ConditionVariable, LoopIncrement) {
268 static const int kMaxThreadCount = 16;
269 Mutex mutex;
270 ConditionVariable cv;
271 for (int thread_count = 1; thread_count < kMaxThreadCount; ++thread_count) {
272 int limit = thread_count * 10;
273 int counter = 0;
274
275 // Setup the threads.
276 Thread** threads = new Thread* [thread_count];
277 for (int n = 0; n < thread_count; ++n) {
278 threads[n] = new LoopIncrementThread(n, &counter, limit, thread_count,
279 &cv, &mutex);
280 }
281
282 // Start all threads.
283 for (int n = thread_count - 1; n >= 0; --n) {
284 threads[n]->Start();
285 }
286
287 // Join and cleanup all threads.
288 for (int n = 0; n < thread_count; ++n) {
289 threads[n]->Join();
290 delete threads[n];
291 }
292 delete[] threads;
293
294 EXPECT_EQ(limit, counter);
295 }
296 }
297
298 } // namespace base
299 } // namespace v8
300