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
27namespace webrtc {
28
29namespace {
30
31const 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.
37static 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
43class ProtectedCount {
44 public:
45  explicit ProtectedCount(CriticalSectionWrapper* crit_sect)
46    : crit_sect_(crit_sect),
47      count_(0) {
48  }
49
50  void Increment() {
51    CriticalSectionScoped cs(crit_sect_);
52    ++count_;
53    LOG("Inc to %d", count_);
54  }
55
56  int Count() const {
57    CriticalSectionScoped cs(crit_sect_);
58    return count_;
59  }
60
61 private:
62  CriticalSectionWrapper* crit_sect_;
63  int count_;
64};
65
66class CritSectTest : public ::testing::Test {
67 public:
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.
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
90bool 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
99TEST_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
121bool 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
131TEST_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