Home | History | Annotate | Download | only in platform
      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