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.
SwitchProcess()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:
ProtectedCount(CriticalSectionWrapper * crit_sect)45 explicit ProtectedCount(CriticalSectionWrapper* crit_sect)
46 : crit_sect_(crit_sect),
47 count_(0) {
48 }
49
Increment()50 void Increment() {
51 CriticalSectionScoped cs(crit_sect_);
52 ++count_;
53 LOG("Inc to %d", count_);
54 }
55
Count() const56 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:
CritSectTest()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.
WaitForCount(int target,ProtectedCount * count)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
LockUnlockThenStopRunFunction(void * obj)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
TEST_F(CritSectTest,ThreadWakesOnce)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
LockUnlockRunFunction(void * obj)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
TEST_F(CritSectTest,ThreadWakesTwice)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