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 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: 34 ThreadWithMutexAndConditionVariable() 35 : Thread(Options("ThreadWithMutexAndConditionVariable")), 36 running_(false), 37 finished_(false) {} 38 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 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: 112 ThreadWithSharedMutexAndConditionVariable() 113 : Thread(Options("ThreadWithSharedMutexAndConditionVariable")), 114 running_(false), 115 finished_(false), 116 cv_(NULL), 117 mutex_(NULL) {} 118 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 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: 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 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 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