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