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
11namespace v8 {
12namespace base {
13
14TEST(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
30namespace {
31
32class ThreadWithMutexAndConditionVariable FINAL : public Thread {
33 public:
34  ThreadWithMutexAndConditionVariable()
35      : Thread(Options("ThreadWithMutexAndConditionVariable")),
36        running_(false),
37        finished_(false) {}
38  virtual ~ThreadWithMutexAndConditionVariable() {}
39
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
60TEST(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
109namespace {
110
111class ThreadWithSharedMutexAndConditionVariable FINAL : public Thread {
112 public:
113  ThreadWithSharedMutexAndConditionVariable()
114      : Thread(Options("ThreadWithSharedMutexAndConditionVariable")),
115        running_(false),
116        finished_(false),
117        cv_(NULL),
118        mutex_(NULL) {}
119  virtual ~ThreadWithSharedMutexAndConditionVariable() {}
120
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
141TEST(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
219namespace {
220
221class LoopIncrementThread FINAL : public Thread {
222 public:
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
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
269TEST(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