blob: f375236913429d1f936fddfda841813c2debfb44 [file] [log] [blame]
hta@webrtc.org3168e532012-06-20 06:45:56 +00001/*
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
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +000011#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
12
hta@webrtc.org3168e532012-06-20 06:45:56 +000013#ifdef _WIN32
14// For Sleep()
15#include <windows.h>
16#else
17// For nanosleep()
18#include <time.h>
19#endif
20
hta@webrtc.org3168e532012-06-20 06:45:56 +000021#include "gtest/gtest.h"
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +000022#include "webrtc/system_wrappers/interface/sleep.h"
23#include "webrtc/system_wrappers/interface/thread_wrapper.h"
24#include "webrtc/system_wrappers/interface/trace.h"
25#include "webrtc/system_wrappers/source/unittest_utilities.h"
hta@webrtc.org3168e532012-06-20 06:45:56 +000026
27namespace webrtc {
28
29namespace {
30
31const bool kLogTrace = false; // Set to true to enable debug logging to stdout.
32
hta@webrtc.org3168e532012-06-20 06:45:56 +000033// Cause a process switch. Needed to avoid depending on
34// busy-wait in tests.
35static void SwitchProcess() {
36 // Note - sched_yield has been tried as process switch. This does
37 // not cause a process switch enough of the time for reliability.
38 SleepMs(1);
39}
40
41class ProtectedCount {
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +000042public:
hta@webrtc.org3168e532012-06-20 06:45:56 +000043 explicit ProtectedCount(CriticalSectionWrapper* crit_sect)
44 : crit_sect_(crit_sect),
45 count_(0) {
46 }
47
48 void Increment() {
49 CriticalSectionScoped cs(crit_sect_);
50 ++count_;
hta@webrtc.org3168e532012-06-20 06:45:56 +000051 }
52
53 int Count() const {
54 CriticalSectionScoped cs(crit_sect_);
55 return count_;
56 }
57
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +000058private:
hta@webrtc.org3168e532012-06-20 06:45:56 +000059 CriticalSectionWrapper* crit_sect_;
60 int count_;
61};
62
63class CritSectTest : public ::testing::Test {
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +000064public:
hta@webrtc.org3168e532012-06-20 06:45:56 +000065 CritSectTest() : trace_(kLogTrace) {
66 }
67
68 // Waits a number of cycles for the count to reach a given value.
69 // Returns true if the target is reached or passed.
70 bool WaitForCount(int target, ProtectedCount* count) {
71 int loop_counter = 0;
72 // On Posix, this SwitchProcess() needs to be in a loop to make the
73 // test both fast and non-flaky.
74 // With 1 us wait as the switch, up to 7 rounds have been observed.
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +000075 while (count->Count() < target && loop_counter < 100 * target) {
hta@webrtc.org3168e532012-06-20 06:45:56 +000076 ++loop_counter;
77 SwitchProcess();
78 }
hta@webrtc.org3168e532012-06-20 06:45:56 +000079 return (count->Count() >= target);
80 }
81
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +000082private:
hta@webrtc.org3168e532012-06-20 06:45:56 +000083 ScopedTracing trace_;
84};
85
86bool LockUnlockThenStopRunFunction(void* obj) {
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +000087 ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
hta@webrtc.org3168e532012-06-20 06:45:56 +000088 the_count->Increment();
hta@webrtc.org3168e532012-06-20 06:45:56 +000089 return false;
90}
91
92TEST_F(CritSectTest, ThreadWakesOnce) {
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +000093 CriticalSectionWrapper* crit_sect =
94 CriticalSectionWrapper::CreateCriticalSection();
hta@webrtc.org3168e532012-06-20 06:45:56 +000095 ProtectedCount count(crit_sect);
96 ThreadWrapper* thread = ThreadWrapper::CreateThread(
97 &LockUnlockThenStopRunFunction, &count);
98 unsigned int id = 42;
99 crit_sect->Enter();
100 ASSERT_TRUE(thread->Start(id));
101 SwitchProcess();
102 // The critical section is of reentrant mode, so this should not release
103 // the lock, even though count.Count() locks and unlocks the critical section
104 // again.
105 // Thus, the thread should not be able to increment the count
106 ASSERT_EQ(0, count.Count());
107 crit_sect->Leave(); // This frees the thread to act.
108 EXPECT_TRUE(WaitForCount(1, &count));
109 EXPECT_TRUE(thread->Stop());
110 delete thread;
111 delete crit_sect;
112}
113
114bool LockUnlockRunFunction(void* obj) {
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +0000115 ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
hta@webrtc.org3168e532012-06-20 06:45:56 +0000116 the_count->Increment();
117 SwitchProcess();
hta@webrtc.org3168e532012-06-20 06:45:56 +0000118 return true;
119}
120
121TEST_F(CritSectTest, ThreadWakesTwice) {
phoglund@webrtc.org99f7c912012-11-30 10:44:49 +0000122 CriticalSectionWrapper* crit_sect =
123 CriticalSectionWrapper::CreateCriticalSection();
hta@webrtc.org3168e532012-06-20 06:45:56 +0000124 ProtectedCount count(crit_sect);
125 ThreadWrapper* thread = ThreadWrapper::CreateThread(&LockUnlockRunFunction,
126 &count);
127 unsigned int id = 42;
128 crit_sect->Enter(); // Make sure counter stays 0 until we wait for it.
129 ASSERT_TRUE(thread->Start(id));
130 crit_sect->Leave();
131
132 // The thread is capable of grabbing the lock multiple times,
133 // incrementing counter once each time.
134 // It's possible for the count to be incremented by more than 2.
135 EXPECT_TRUE(WaitForCount(2, &count));
136 EXPECT_LE(2, count.Count());
137
138 // The thread does not increment while lock is held.
139 crit_sect->Enter();
140 int count_before = count.Count();
141 for (int i = 0; i < 10; i++) {
142 SwitchProcess();
143 }
144 EXPECT_EQ(count_before, count.Count());
145 crit_sect->Leave();
146
147 thread->SetNotAlive(); // Tell thread to exit once run function finishes.
148 SwitchProcess();
149 EXPECT_LT(count_before, count.Count());
150 EXPECT_TRUE(thread->Stop());
151 delete thread;
152 delete crit_sect;
153}
154
155} // anonymous namespace
156
157} // namespace webrtc