| /* |
| * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/system_wrappers/include/critical_section_wrapper.h" |
| |
| #include "webrtc/test/gtest.h" |
| #include "webrtc/system_wrappers/include/sleep.h" |
| #include "webrtc/base/platform_thread.h" |
| #include "webrtc/system_wrappers/include/trace.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| |
| // Cause a process switch. Needed to avoid depending on |
| // busy-wait in tests. |
| static void SwitchProcess() { |
| // Note - sched_yield has been tried as process switch. This does |
| // not cause a process switch enough of the time for reliability. |
| SleepMs(1); |
| } |
| |
| class ProtectedCount { |
| public: |
| explicit ProtectedCount(CriticalSectionWrapper* crit_sect) |
| : crit_sect_(crit_sect), |
| count_(0) { |
| } |
| |
| void Increment() { |
| CriticalSectionScoped cs(crit_sect_); |
| ++count_; |
| } |
| |
| int Count() const { |
| CriticalSectionScoped cs(crit_sect_); |
| return count_; |
| } |
| |
| private: |
| CriticalSectionWrapper* crit_sect_; |
| int count_; |
| }; |
| |
| class CritSectTest : public ::testing::Test { |
| public: |
| CritSectTest() {} |
| |
| // Waits a number of cycles for the count to reach a given value. |
| // Returns true if the target is reached or passed. |
| bool WaitForCount(int target, ProtectedCount* count) { |
| int loop_counter = 0; |
| // On Posix, this SwitchProcess() needs to be in a loop to make the |
| // test both fast and non-flaky. |
| // With 1 us wait as the switch, up to 7 rounds have been observed. |
| while (count->Count() < target && loop_counter < 100 * target) { |
| ++loop_counter; |
| SwitchProcess(); |
| } |
| return (count->Count() >= target); |
| } |
| }; |
| |
| bool LockUnlockThenStopRunFunction(void* obj) { |
| ProtectedCount* the_count = static_cast<ProtectedCount*>(obj); |
| the_count->Increment(); |
| return false; |
| } |
| |
| TEST_F(CritSectTest, ThreadWakesOnce) NO_THREAD_SAFETY_ANALYSIS { |
| CriticalSectionWrapper* crit_sect = |
| CriticalSectionWrapper::CreateCriticalSection(); |
| ProtectedCount count(crit_sect); |
| rtc::PlatformThread thread( |
| &LockUnlockThenStopRunFunction, &count, "ThreadWakesOnce"); |
| crit_sect->Enter(); |
| thread.Start(); |
| SwitchProcess(); |
| // The critical section is of reentrant mode, so this should not release |
| // the lock, even though count.Count() locks and unlocks the critical section |
| // again. |
| // Thus, the thread should not be able to increment the count |
| ASSERT_EQ(0, count.Count()); |
| crit_sect->Leave(); // This frees the thread to act. |
| EXPECT_TRUE(WaitForCount(1, &count)); |
| thread.Stop(); |
| delete crit_sect; |
| } |
| |
| bool LockUnlockRunFunction(void* obj) { |
| ProtectedCount* the_count = static_cast<ProtectedCount*>(obj); |
| the_count->Increment(); |
| SwitchProcess(); |
| return true; |
| } |
| |
| TEST_F(CritSectTest, ThreadWakesTwice) NO_THREAD_SAFETY_ANALYSIS { |
| CriticalSectionWrapper* crit_sect = |
| CriticalSectionWrapper::CreateCriticalSection(); |
| ProtectedCount count(crit_sect); |
| rtc::PlatformThread thread( |
| &LockUnlockRunFunction, &count, "ThreadWakesTwice"); |
| crit_sect->Enter(); // Make sure counter stays 0 until we wait for it. |
| thread.Start(); |
| crit_sect->Leave(); |
| |
| // The thread is capable of grabbing the lock multiple times, |
| // incrementing counter once each time. |
| // It's possible for the count to be incremented by more than 2. |
| EXPECT_TRUE(WaitForCount(2, &count)); |
| EXPECT_LE(2, count.Count()); |
| |
| // The thread does not increment while lock is held. |
| crit_sect->Enter(); |
| int count_before = count.Count(); |
| for (int i = 0; i < 10; i++) { |
| SwitchProcess(); |
| } |
| EXPECT_EQ(count_before, count.Count()); |
| crit_sect->Leave(); |
| |
| SwitchProcess(); |
| EXPECT_TRUE(WaitForCount(count_before + 1, &count)); |
| thread.Stop(); |
| delete crit_sect; |
| } |
| |
| } // anonymous namespace |
| |
| } // namespace webrtc |