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