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