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