• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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