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
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  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
97bool LockUnlockRunFunction(void* obj) {
98  ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
99  the_count->Increment();
100  SwitchProcess();
101  return true;
102}
103
104TEST_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