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