1 // Copyright 2013 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "src/v8.h" 29 30 #include "src/platform/condition-variable.h" 31 #include "src/platform/time.h" 32 #include "test/cctest/cctest.h" 33 34 using namespace ::v8::internal; 35 36 37 TEST(WaitForAfterNofityOnSameThread) { 38 for (int n = 0; n < 10; ++n) { 39 Mutex mutex; 40 ConditionVariable cv; 41 42 LockGuard<Mutex> lock_guard(&mutex); 43 44 cv.NotifyOne(); 45 CHECK_EQ(false, cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n))); 46 47 cv.NotifyAll(); 48 CHECK_EQ(false, cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n))); 49 } 50 } 51 52 53 class ThreadWithMutexAndConditionVariable V8_FINAL : public Thread { 54 public: 55 ThreadWithMutexAndConditionVariable() 56 : Thread("ThreadWithMutexAndConditionVariable"), 57 running_(false), finished_(false) {} 58 virtual ~ThreadWithMutexAndConditionVariable() {} 59 60 virtual void Run() V8_OVERRIDE { 61 LockGuard<Mutex> lock_guard(&mutex_); 62 running_ = true; 63 cv_.NotifyOne(); 64 while (running_) { 65 cv_.Wait(&mutex_); 66 } 67 finished_ = true; 68 cv_.NotifyAll(); 69 } 70 71 bool running_; 72 bool finished_; 73 ConditionVariable cv_; 74 Mutex mutex_; 75 }; 76 77 78 TEST(MultipleThreadsWithSeparateConditionVariables) { 79 static const int kThreadCount = 128; 80 ThreadWithMutexAndConditionVariable threads[kThreadCount]; 81 82 for (int n = 0; n < kThreadCount; ++n) { 83 LockGuard<Mutex> lock_guard(&threads[n].mutex_); 84 CHECK(!threads[n].running_); 85 CHECK(!threads[n].finished_); 86 threads[n].Start(); 87 // Wait for nth thread to start. 88 while (!threads[n].running_) { 89 threads[n].cv_.Wait(&threads[n].mutex_); 90 } 91 } 92 93 for (int n = kThreadCount - 1; n >= 0; --n) { 94 LockGuard<Mutex> lock_guard(&threads[n].mutex_); 95 CHECK(threads[n].running_); 96 CHECK(!threads[n].finished_); 97 } 98 99 for (int n = 0; n < kThreadCount; ++n) { 100 LockGuard<Mutex> lock_guard(&threads[n].mutex_); 101 CHECK(threads[n].running_); 102 CHECK(!threads[n].finished_); 103 // Tell the nth thread to quit. 104 threads[n].running_ = false; 105 threads[n].cv_.NotifyOne(); 106 } 107 108 for (int n = kThreadCount - 1; n >= 0; --n) { 109 // Wait for nth thread to quit. 110 LockGuard<Mutex> lock_guard(&threads[n].mutex_); 111 while (!threads[n].finished_) { 112 threads[n].cv_.Wait(&threads[n].mutex_); 113 } 114 CHECK(!threads[n].running_); 115 CHECK(threads[n].finished_); 116 } 117 118 for (int n = 0; n < kThreadCount; ++n) { 119 threads[n].Join(); 120 LockGuard<Mutex> lock_guard(&threads[n].mutex_); 121 CHECK(!threads[n].running_); 122 CHECK(threads[n].finished_); 123 } 124 } 125 126 127 class ThreadWithSharedMutexAndConditionVariable V8_FINAL : public Thread { 128 public: 129 ThreadWithSharedMutexAndConditionVariable() 130 : Thread("ThreadWithSharedMutexAndConditionVariable"), 131 running_(false), finished_(false), cv_(NULL), mutex_(NULL) {} 132 virtual ~ThreadWithSharedMutexAndConditionVariable() {} 133 134 virtual void Run() V8_OVERRIDE { 135 LockGuard<Mutex> lock_guard(mutex_); 136 running_ = true; 137 cv_->NotifyAll(); 138 while (running_) { 139 cv_->Wait(mutex_); 140 } 141 finished_ = true; 142 cv_->NotifyAll(); 143 } 144 145 bool running_; 146 bool finished_; 147 ConditionVariable* cv_; 148 Mutex* mutex_; 149 }; 150 151 152 TEST(MultipleThreadsWithSharedSeparateConditionVariables) { 153 static const int kThreadCount = 128; 154 ThreadWithSharedMutexAndConditionVariable threads[kThreadCount]; 155 ConditionVariable cv; 156 Mutex mutex; 157 158 for (int n = 0; n < kThreadCount; ++n) { 159 threads[n].mutex_ = &mutex; 160 threads[n].cv_ = &cv; 161 } 162 163 // Start all threads. 164 { 165 LockGuard<Mutex> lock_guard(&mutex); 166 for (int n = 0; n < kThreadCount; ++n) { 167 CHECK(!threads[n].running_); 168 CHECK(!threads[n].finished_); 169 threads[n].Start(); 170 } 171 } 172 173 // Wait for all threads to start. 174 { 175 LockGuard<Mutex> lock_guard(&mutex); 176 for (int n = kThreadCount - 1; n >= 0; --n) { 177 while (!threads[n].running_) { 178 cv.Wait(&mutex); 179 } 180 } 181 } 182 183 // Make sure that all threads are running. 184 { 185 LockGuard<Mutex> lock_guard(&mutex); 186 for (int n = 0; n < kThreadCount; ++n) { 187 CHECK(threads[n].running_); 188 CHECK(!threads[n].finished_); 189 } 190 } 191 192 // Tell all threads to quit. 193 { 194 LockGuard<Mutex> lock_guard(&mutex); 195 for (int n = kThreadCount - 1; n >= 0; --n) { 196 CHECK(threads[n].running_); 197 CHECK(!threads[n].finished_); 198 // Tell the nth thread to quit. 199 threads[n].running_ = false; 200 } 201 cv.NotifyAll(); 202 } 203 204 // Wait for all threads to quit. 205 { 206 LockGuard<Mutex> lock_guard(&mutex); 207 for (int n = 0; n < kThreadCount; ++n) { 208 while (!threads[n].finished_) { 209 cv.Wait(&mutex); 210 } 211 } 212 } 213 214 // Make sure all threads are finished. 215 { 216 LockGuard<Mutex> lock_guard(&mutex); 217 for (int n = kThreadCount - 1; n >= 0; --n) { 218 CHECK(!threads[n].running_); 219 CHECK(threads[n].finished_); 220 } 221 } 222 223 // Join all threads. 224 for (int n = 0; n < kThreadCount; ++n) { 225 threads[n].Join(); 226 } 227 } 228 229 230 class LoopIncrementThread V8_FINAL : public Thread { 231 public: 232 LoopIncrementThread(int rem, 233 int* counter, 234 int limit, 235 int thread_count, 236 ConditionVariable* cv, 237 Mutex* mutex) 238 : Thread("LoopIncrementThread"), rem_(rem), counter_(counter), 239 limit_(limit), thread_count_(thread_count), cv_(cv), mutex_(mutex) { 240 CHECK_LT(rem, thread_count); 241 CHECK_EQ(0, limit % thread_count); 242 } 243 244 virtual void Run() V8_OVERRIDE { 245 int last_count = -1; 246 while (true) { 247 LockGuard<Mutex> lock_guard(mutex_); 248 int count = *counter_; 249 while (count % thread_count_ != rem_ && count < limit_) { 250 cv_->Wait(mutex_); 251 count = *counter_; 252 } 253 if (count >= limit_) break; 254 CHECK_EQ(*counter_, count); 255 if (last_count != -1) { 256 CHECK_EQ(last_count + (thread_count_ - 1), count); 257 } 258 count++; 259 *counter_ = count; 260 last_count = count; 261 cv_->NotifyAll(); 262 } 263 } 264 265 private: 266 const int rem_; 267 int* counter_; 268 const int limit_; 269 const int thread_count_; 270 ConditionVariable* cv_; 271 Mutex* mutex_; 272 }; 273 274 275 TEST(LoopIncrement) { 276 static const int kMaxThreadCount = 16; 277 Mutex mutex; 278 ConditionVariable cv; 279 for (int thread_count = 1; thread_count < kMaxThreadCount; ++thread_count) { 280 int limit = thread_count * 100; 281 int counter = 0; 282 283 // Setup the threads. 284 Thread** threads = new Thread*[thread_count]; 285 for (int n = 0; n < thread_count; ++n) { 286 threads[n] = new LoopIncrementThread( 287 n, &counter, limit, thread_count, &cv, &mutex); 288 } 289 290 // Start all threads. 291 for (int n = thread_count - 1; n >= 0; --n) { 292 threads[n]->Start(); 293 } 294 295 // Join and cleanup all threads. 296 for (int n = 0; n < thread_count; ++n) { 297 threads[n]->Join(); 298 delete threads[n]; 299 } 300 delete[] threads; 301 302 CHECK_EQ(limit, counter); 303 } 304 } 305