Home | History | Annotate | Download | only in source
      1 /*
      2  *  Copyright (c) 2012 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 "webrtc/system_wrappers/interface/critical_section_wrapper.h"
     12 
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 #include "webrtc/system_wrappers/interface/sleep.h"
     15 #include "webrtc/system_wrappers/interface/thread_wrapper.h"
     16 #include "webrtc/system_wrappers/interface/trace.h"
     17 #include "webrtc/system_wrappers/source/unittest_utilities.h"
     18 
     19 namespace webrtc {
     20 
     21 namespace {
     22 
     23 const bool kLogTrace = false;  // Set to true to enable debug logging to stdout.
     24 
     25 // Cause a process switch. Needed to avoid depending on
     26 // busy-wait in tests.
     27 static void SwitchProcess() {
     28   // Note - sched_yield has been tried as process switch. This does
     29   // not cause a process switch enough of the time for reliability.
     30   SleepMs(1);
     31 }
     32 
     33 class ProtectedCount {
     34 public:
     35   explicit ProtectedCount(CriticalSectionWrapper* crit_sect)
     36     : crit_sect_(crit_sect),
     37       count_(0) {
     38   }
     39 
     40   void Increment() {
     41     CriticalSectionScoped cs(crit_sect_);
     42     ++count_;
     43   }
     44 
     45   int Count() const {
     46     CriticalSectionScoped cs(crit_sect_);
     47     return count_;
     48   }
     49 
     50 private:
     51   CriticalSectionWrapper* crit_sect_;
     52   int count_;
     53 };
     54 
     55 class CritSectTest : public ::testing::Test {
     56 public:
     57   CritSectTest() : trace_(kLogTrace) {
     58   }
     59 
     60   // Waits a number of cycles for the count to reach a given value.
     61   // Returns true if the target is reached or passed.
     62   bool WaitForCount(int target, ProtectedCount* count) {
     63     int loop_counter = 0;
     64     // On Posix, this SwitchProcess() needs to be in a loop to make the
     65     // test both fast and non-flaky.
     66     // With 1 us wait as the switch, up to 7 rounds have been observed.
     67     while (count->Count() < target && loop_counter < 100 * target) {
     68       ++loop_counter;
     69       SwitchProcess();
     70     }
     71     return (count->Count() >= target);
     72   }
     73 
     74 private:
     75   ScopedTracing trace_;
     76 };
     77 
     78 bool LockUnlockThenStopRunFunction(void* obj) {
     79   ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
     80   the_count->Increment();
     81   return false;
     82 }
     83 
     84 TEST_F(CritSectTest, ThreadWakesOnce) NO_THREAD_SAFETY_ANALYSIS {
     85   CriticalSectionWrapper* crit_sect =
     86       CriticalSectionWrapper::CreateCriticalSection();
     87   ProtectedCount count(crit_sect);
     88   ThreadWrapper* thread = ThreadWrapper::CreateThread(
     89       &LockUnlockThenStopRunFunction, &count);
     90   unsigned int id = 42;
     91   crit_sect->Enter();
     92   ASSERT_TRUE(thread->Start(id));
     93   SwitchProcess();
     94   // The critical section is of reentrant mode, so this should not release
     95   // the lock, even though count.Count() locks and unlocks the critical section
     96   // again.
     97   // Thus, the thread should not be able to increment the count
     98   ASSERT_EQ(0, count.Count());
     99   crit_sect->Leave();  // This frees the thread to act.
    100   EXPECT_TRUE(WaitForCount(1, &count));
    101   EXPECT_TRUE(thread->Stop());
    102   delete thread;
    103   delete crit_sect;
    104 }
    105 
    106 bool LockUnlockRunFunction(void* obj) {
    107   ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
    108   the_count->Increment();
    109   SwitchProcess();
    110   return true;
    111 }
    112 
    113 TEST_F(CritSectTest, ThreadWakesTwice) NO_THREAD_SAFETY_ANALYSIS {
    114   CriticalSectionWrapper* crit_sect =
    115       CriticalSectionWrapper::CreateCriticalSection();
    116   ProtectedCount count(crit_sect);
    117   ThreadWrapper* thread = ThreadWrapper::CreateThread(&LockUnlockRunFunction,
    118                                                       &count);
    119   unsigned int id = 42;
    120   crit_sect->Enter();  // Make sure counter stays 0 until we wait for it.
    121   ASSERT_TRUE(thread->Start(id));
    122   crit_sect->Leave();
    123 
    124   // The thread is capable of grabbing the lock multiple times,
    125   // incrementing counter once each time.
    126   // It's possible for the count to be incremented by more than 2.
    127   EXPECT_TRUE(WaitForCount(2, &count));
    128   EXPECT_LE(2, count.Count());
    129 
    130   // The thread does not increment while lock is held.
    131   crit_sect->Enter();
    132   int count_before = count.Count();
    133   for (int i = 0; i < 10; i++) {
    134     SwitchProcess();
    135   }
    136   EXPECT_EQ(count_before, count.Count());
    137   crit_sect->Leave();
    138 
    139   thread->SetNotAlive();  // Tell thread to exit once run function finishes.
    140   SwitchProcess();
    141   EXPECT_TRUE(WaitForCount(count_before + 1, &count));
    142   EXPECT_TRUE(thread->Stop());
    143   delete thread;
    144   delete crit_sect;
    145 }
    146 
    147 }  // anonymous namespace
    148 
    149 }  // namespace webrtc
    150