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.
SwitchProcess()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:
ProtectedCount(CriticalSectionWrapper * crit_sect)32 explicit ProtectedCount(CriticalSectionWrapper* crit_sect)
33 : crit_sect_(crit_sect),
34 count_(0) {
35 }
36
Increment()37 void Increment() {
38 CriticalSectionScoped cs(crit_sect_);
39 ++count_;
40 }
41
Count() const42 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:
CritSectTest()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.
WaitForCount(int target,ProtectedCount * count)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
LockUnlockThenStopRunFunction(void * obj)71 bool LockUnlockThenStopRunFunction(void* obj) {
72 ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
73 the_count->Increment();
74 return false;
75 }
76
TEST_F(CritSectTest,ThreadWakesOnce)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
LockUnlockRunFunction(void * obj)97 bool LockUnlockRunFunction(void* obj) {
98 ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
99 the_count->Increment();
100 SwitchProcess();
101 return true;
102 }
103
TEST_F(CritSectTest,ThreadWakesTwice)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