critical_section_unittest.cc revision 99f7c917d2933005a7d41f6d85cc9231d0395669
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#ifdef _WIN32
14// For Sleep()
15#include <windows.h>
16#else
17// For nanosleep()
18#include <time.h>
19#endif
20
21#include "gtest/gtest.h"
22#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"
26
27namespace webrtc {
28
29namespace {
30
31const bool kLogTrace = false;  // Set to true to enable debug logging to stdout.
32
33// 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 {
42public:
43  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_;
51  }
52
53  int Count() const {
54    CriticalSectionScoped cs(crit_sect_);
55    return count_;
56  }
57
58private:
59  CriticalSectionWrapper* crit_sect_;
60  int count_;
61};
62
63class CritSectTest : public ::testing::Test {
64public:
65  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.
75    while (count->Count() < target && loop_counter < 100 * target) {
76      ++loop_counter;
77      SwitchProcess();
78    }
79    return (count->Count() >= target);
80  }
81
82private:
83  ScopedTracing trace_;
84};
85
86bool LockUnlockThenStopRunFunction(void* obj) {
87  ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
88  the_count->Increment();
89  return false;
90}
91
92TEST_F(CritSectTest, ThreadWakesOnce) {
93  CriticalSectionWrapper* crit_sect =
94      CriticalSectionWrapper::CreateCriticalSection();
95  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) {
115  ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
116  the_count->Increment();
117  SwitchProcess();
118  return true;
119}
120
121TEST_F(CritSectTest, ThreadWakesTwice) {
122  CriticalSectionWrapper* crit_sect =
123      CriticalSectionWrapper::CreateCriticalSection();
124  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
158