Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include <set>
     12 #include <vector>
     13 
     14 #include "webrtc/base/criticalsection.h"
     15 #include "webrtc/base/event.h"
     16 #include "webrtc/base/gunit.h"
     17 #include "webrtc/base/scoped_ptr.h"
     18 #include "webrtc/base/scopedptrcollection.h"
     19 #include "webrtc/base/thread.h"
     20 
     21 namespace rtc {
     22 
     23 namespace {
     24 
     25 const int kLongTime = 10000;  // 10 seconds
     26 const int kNumThreads = 16;
     27 const int kOperationsToRun = 1000;
     28 
     29 class UniqueValueVerifier {
     30  public:
     31   void Verify(const std::vector<int>& values) {
     32     for (size_t i = 0; i < values.size(); ++i) {
     33       std::pair<std::set<int>::iterator, bool> result =
     34           all_values_.insert(values[i]);
     35       // Each value should only be taken by one thread, so if this value
     36       // has already been added, something went wrong.
     37       EXPECT_TRUE(result.second)
     38           << " Thread=" << Thread::Current() << " value=" << values[i];
     39     }
     40   }
     41 
     42   void Finalize() {}
     43 
     44  private:
     45   std::set<int> all_values_;
     46 };
     47 
     48 class CompareAndSwapVerifier {
     49  public:
     50   CompareAndSwapVerifier() : zero_count_(0) {}
     51 
     52   void Verify(const std::vector<int>& values) {
     53     for (auto v : values) {
     54       if (v == 0) {
     55         EXPECT_EQ(0, zero_count_) << "Thread=" << Thread::Current();
     56         ++zero_count_;
     57       } else {
     58         EXPECT_EQ(1, v) << " Thread=" << Thread::Current();
     59       }
     60     }
     61   }
     62 
     63   void Finalize() {
     64     EXPECT_EQ(1, zero_count_);
     65   }
     66  private:
     67   int zero_count_;
     68 };
     69 
     70 class RunnerBase : public MessageHandler {
     71  public:
     72   explicit RunnerBase(int value)
     73       : threads_active_(0),
     74         start_event_(true, false),
     75         done_event_(true, false),
     76         shared_value_(value) {}
     77 
     78   bool Run() {
     79     // Signal all threads to start.
     80     start_event_.Set();
     81 
     82     // Wait for all threads to finish.
     83     return done_event_.Wait(kLongTime);
     84   }
     85 
     86   void SetExpectedThreadCount(int count) {
     87     threads_active_ = count;
     88   }
     89 
     90   int shared_value() const { return shared_value_; }
     91 
     92  protected:
     93   // Derived classes must override OnMessage, and call BeforeStart and AfterEnd
     94   // at the beginning and the end of OnMessage respectively.
     95   void BeforeStart() {
     96     ASSERT_TRUE(start_event_.Wait(kLongTime));
     97   }
     98 
     99   // Returns true if all threads have finished.
    100   bool AfterEnd() {
    101     if (AtomicOps::Decrement(&threads_active_) == 0) {
    102       done_event_.Set();
    103       return true;
    104     }
    105     return false;
    106   }
    107 
    108   int threads_active_;
    109   Event start_event_;
    110   Event done_event_;
    111   int shared_value_;
    112 };
    113 
    114 class LOCKABLE CriticalSectionLock {
    115  public:
    116   void Lock() EXCLUSIVE_LOCK_FUNCTION() {
    117     cs_.Enter();
    118   }
    119   void Unlock() UNLOCK_FUNCTION() {
    120     cs_.Leave();
    121   }
    122 
    123  private:
    124   CriticalSection cs_;
    125 };
    126 
    127 template <class Lock>
    128 class LockRunner : public RunnerBase {
    129  public:
    130   LockRunner() : RunnerBase(0) {}
    131 
    132   void OnMessage(Message* msg) override {
    133     BeforeStart();
    134 
    135     lock_.Lock();
    136 
    137     EXPECT_EQ(0, shared_value_);
    138     int old = shared_value_;
    139 
    140     // Use a loop to increase the chance of race.
    141     for (int i = 0; i < kOperationsToRun; ++i) {
    142       ++shared_value_;
    143     }
    144     EXPECT_EQ(old + kOperationsToRun, shared_value_);
    145     shared_value_ = 0;
    146 
    147     lock_.Unlock();
    148 
    149     AfterEnd();
    150   }
    151 
    152  private:
    153   Lock lock_;
    154 };
    155 
    156 template <class Op, class Verifier>
    157 class AtomicOpRunner : public RunnerBase {
    158  public:
    159   explicit AtomicOpRunner(int initial_value) : RunnerBase(initial_value) {}
    160 
    161   void OnMessage(Message* msg) override {
    162     BeforeStart();
    163 
    164     std::vector<int> values;
    165     values.reserve(kOperationsToRun);
    166 
    167     // Generate a bunch of values by updating shared_value_ atomically.
    168     for (int i = 0; i < kOperationsToRun; ++i) {
    169       values.push_back(Op::AtomicOp(&shared_value_));
    170     }
    171 
    172     { // Add them all to the set.
    173       CritScope cs(&all_values_crit_);
    174       verifier_.Verify(values);
    175     }
    176 
    177     if (AfterEnd()) {
    178       verifier_.Finalize();
    179     }
    180   }
    181 
    182  private:
    183   CriticalSection all_values_crit_;
    184   Verifier verifier_;
    185 };
    186 
    187 struct IncrementOp {
    188   static int AtomicOp(int* i) { return AtomicOps::Increment(i); }
    189 };
    190 
    191 struct DecrementOp {
    192   static int AtomicOp(int* i) { return AtomicOps::Decrement(i); }
    193 };
    194 
    195 struct CompareAndSwapOp {
    196   static int AtomicOp(int* i) { return AtomicOps::CompareAndSwap(i, 0, 1); }
    197 };
    198 
    199 void StartThreads(ScopedPtrCollection<Thread>* threads,
    200                   MessageHandler* handler) {
    201   for (int i = 0; i < kNumThreads; ++i) {
    202     Thread* thread = new Thread();
    203     thread->Start();
    204     thread->Post(handler);
    205     threads->PushBack(thread);
    206   }
    207 }
    208 
    209 }  // namespace
    210 
    211 TEST(AtomicOpsTest, Simple) {
    212   int value = 0;
    213   EXPECT_EQ(1, AtomicOps::Increment(&value));
    214   EXPECT_EQ(1, value);
    215   EXPECT_EQ(2, AtomicOps::Increment(&value));
    216   EXPECT_EQ(2, value);
    217   EXPECT_EQ(1, AtomicOps::Decrement(&value));
    218   EXPECT_EQ(1, value);
    219   EXPECT_EQ(0, AtomicOps::Decrement(&value));
    220   EXPECT_EQ(0, value);
    221 }
    222 
    223 TEST(AtomicOpsTest, SimplePtr) {
    224   class Foo {};
    225   Foo* volatile foo = nullptr;
    226   scoped_ptr<Foo> a(new Foo());
    227   scoped_ptr<Foo> b(new Foo());
    228   // Reading the initial value should work as expected.
    229   EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == nullptr);
    230   // Setting using compare and swap should work.
    231   EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
    232                   &foo, static_cast<Foo*>(nullptr), a.get()) == nullptr);
    233   EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
    234   // Setting another value but with the wrong previous pointer should fail
    235   // (remain a).
    236   EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
    237                   &foo, static_cast<Foo*>(nullptr), b.get()) == a.get());
    238   EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
    239   // Replacing a with b should work.
    240   EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(&foo, a.get(), b.get()) ==
    241               a.get());
    242   EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == b.get());
    243 }
    244 
    245 TEST(AtomicOpsTest, Increment) {
    246   // Create and start lots of threads.
    247   AtomicOpRunner<IncrementOp, UniqueValueVerifier> runner(0);
    248   ScopedPtrCollection<Thread> threads;
    249   StartThreads(&threads, &runner);
    250   runner.SetExpectedThreadCount(kNumThreads);
    251 
    252   // Release the hounds!
    253   EXPECT_TRUE(runner.Run());
    254   EXPECT_EQ(kOperationsToRun * kNumThreads, runner.shared_value());
    255 }
    256 
    257 TEST(AtomicOpsTest, Decrement) {
    258   // Create and start lots of threads.
    259   AtomicOpRunner<DecrementOp, UniqueValueVerifier> runner(
    260       kOperationsToRun * kNumThreads);
    261   ScopedPtrCollection<Thread> threads;
    262   StartThreads(&threads, &runner);
    263   runner.SetExpectedThreadCount(kNumThreads);
    264 
    265   // Release the hounds!
    266   EXPECT_TRUE(runner.Run());
    267   EXPECT_EQ(0, runner.shared_value());
    268 }
    269 
    270 TEST(AtomicOpsTest, CompareAndSwap) {
    271   // Create and start lots of threads.
    272   AtomicOpRunner<CompareAndSwapOp, CompareAndSwapVerifier> runner(0);
    273   ScopedPtrCollection<Thread> threads;
    274   StartThreads(&threads, &runner);
    275   runner.SetExpectedThreadCount(kNumThreads);
    276 
    277   // Release the hounds!
    278   EXPECT_TRUE(runner.Run());
    279   EXPECT_EQ(1, runner.shared_value());
    280 }
    281 
    282 TEST(GlobalLockTest, Basic) {
    283   // Create and start lots of threads.
    284   LockRunner<GlobalLock> runner;
    285   ScopedPtrCollection<Thread> threads;
    286   StartThreads(&threads, &runner);
    287   runner.SetExpectedThreadCount(kNumThreads);
    288 
    289   // Release the hounds!
    290   EXPECT_TRUE(runner.Run());
    291   EXPECT_EQ(0, runner.shared_value());
    292 }
    293 
    294 TEST(CriticalSectionTest, Basic) {
    295   // Create and start lots of threads.
    296   LockRunner<CriticalSectionLock> runner;
    297   ScopedPtrCollection<Thread> threads;
    298   StartThreads(&threads, &runner);
    299   runner.SetExpectedThreadCount(kNumThreads);
    300 
    301   // Release the hounds!
    302   EXPECT_TRUE(runner.Run());
    303   EXPECT_EQ(0, runner.shared_value());
    304 }
    305 
    306 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
    307 TEST(CriticalSectionTest, IsLocked) {
    308   // Simple single-threaded test of IsLocked.
    309   CriticalSection cs;
    310   EXPECT_FALSE(cs.IsLocked());
    311   cs.Enter();
    312   EXPECT_TRUE(cs.IsLocked());
    313   cs.Leave();
    314   EXPECT_FALSE(cs.IsLocked());
    315   if (!cs.TryEnter())
    316     FAIL();
    317   EXPECT_TRUE(cs.IsLocked());
    318   cs.Leave();
    319   EXPECT_FALSE(cs.IsLocked());
    320 }
    321 #endif
    322 
    323 }  // namespace rtc
    324