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