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