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/interface/critical_section_wrapper.h"
12
13#include "testing/gtest/include/gtest/gtest.h"
14#include "webrtc/system_wrappers/interface/sleep.h"
15#include "webrtc/system_wrappers/interface/thread_wrapper.h"
16#include "webrtc/system_wrappers/interface/trace.h"
17
18namespace webrtc {
19
20namespace {
21
22// Cause a process switch. Needed to avoid depending on
23// busy-wait in tests.
24static 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
30class ProtectedCount {
31public:
32  explicit ProtectedCount(CriticalSectionWrapper* crit_sect)
33    : crit_sect_(crit_sect),
34      count_(0) {
35  }
36
37  void Increment() {
38    CriticalSectionScoped cs(crit_sect_);
39    ++count_;
40  }
41
42  int Count() const {
43    CriticalSectionScoped cs(crit_sect_);
44    return count_;
45  }
46
47private:
48  CriticalSectionWrapper* crit_sect_;
49  int count_;
50};
51
52class CritSectTest : public ::testing::Test {
53public:
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.
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
71bool LockUnlockThenStopRunFunction(void* obj) {
72  ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
73  the_count->Increment();
74  return false;
75}
76
77TEST_F(CritSectTest, ThreadWakesOnce) NO_THREAD_SAFETY_ANALYSIS {
78  CriticalSectionWrapper* crit_sect =
79      CriticalSectionWrapper::CreateCriticalSection();
80  ProtectedCount count(crit_sect);
81  ThreadWrapper* thread = ThreadWrapper::CreateThread(
82      &LockUnlockThenStopRunFunction, &count);
83  unsigned int id = 42;
84  crit_sect->Enter();
85  ASSERT_TRUE(thread->Start(id));
86  SwitchProcess();
87  // The critical section is of reentrant mode, so this should not release
88  // the lock, even though count.Count() locks and unlocks the critical section
89  // again.
90  // Thus, the thread should not be able to increment the count
91  ASSERT_EQ(0, count.Count());
92  crit_sect->Leave();  // This frees the thread to act.
93  EXPECT_TRUE(WaitForCount(1, &count));
94  EXPECT_TRUE(thread->Stop());
95  delete thread;
96  delete crit_sect;
97}
98
99bool LockUnlockRunFunction(void* obj) {
100  ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
101  the_count->Increment();
102  SwitchProcess();
103  return true;
104}
105
106TEST_F(CritSectTest, ThreadWakesTwice) NO_THREAD_SAFETY_ANALYSIS {
107  CriticalSectionWrapper* crit_sect =
108      CriticalSectionWrapper::CreateCriticalSection();
109  ProtectedCount count(crit_sect);
110  ThreadWrapper* thread = ThreadWrapper::CreateThread(&LockUnlockRunFunction,
111                                                      &count);
112  unsigned int id = 42;
113  crit_sect->Enter();  // Make sure counter stays 0 until we wait for it.
114  ASSERT_TRUE(thread->Start(id));
115  crit_sect->Leave();
116
117  // The thread is capable of grabbing the lock multiple times,
118  // incrementing counter once each time.
119  // It's possible for the count to be incremented by more than 2.
120  EXPECT_TRUE(WaitForCount(2, &count));
121  EXPECT_LE(2, count.Count());
122
123  // The thread does not increment while lock is held.
124  crit_sect->Enter();
125  int count_before = count.Count();
126  for (int i = 0; i < 10; i++) {
127    SwitchProcess();
128  }
129  EXPECT_EQ(count_before, count.Count());
130  crit_sect->Leave();
131
132  thread->SetNotAlive();  // Tell thread to exit once run function finishes.
133  SwitchProcess();
134  EXPECT_TRUE(WaitForCount(count_before + 1, &count));
135  EXPECT_TRUE(thread->Stop());
136  delete thread;
137  delete crit_sect;
138}
139
140}  // anonymous namespace
141
142}  // namespace webrtc
143