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