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