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