1b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org/*
2b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org *
4b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org *  Use of this source code is governed by a BSD-style license
5b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org *  that can be found in the LICENSE file in the root of the source
6b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org *  tree. An additional intellectual property rights grant can be found
7b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org *  in the file PATENTS.  All contributing project authors may
8b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org *  be found in the AUTHORS file in the root of the source tree.
9b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org */
10b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
11328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
12328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org
13c6d6fed3c0a82bb7a09095381b974e8e5eebcb35pbos@webrtc.org#include "testing/gtest/include/gtest/gtest.h"
14328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org#include "webrtc/system_wrappers/interface/sleep.h"
15328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org#include "webrtc/system_wrappers/interface/thread_wrapper.h"
16328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org#include "webrtc/system_wrappers/interface/trace.h"
17b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
18b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgnamespace webrtc {
19b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
20b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgnamespace {
21b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
22b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org// Cause a process switch. Needed to avoid depending on
23b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org// busy-wait in tests.
24b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgstatic void SwitchProcess() {
25b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // Note - sched_yield has been tried as process switch. This does
26b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // not cause a process switch enough of the time for reliability.
27b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  SleepMs(1);
28b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
29b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
30b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgclass ProtectedCount {
31328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.orgpublic:
32b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  explicit ProtectedCount(CriticalSectionWrapper* crit_sect)
33b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    : crit_sect_(crit_sect),
34b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      count_(0) {
35b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  }
36b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
37b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  void Increment() {
38b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    CriticalSectionScoped cs(crit_sect_);
39b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    ++count_;
40b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  }
41b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
42b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  int Count() const {
43b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    CriticalSectionScoped cs(crit_sect_);
44b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    return count_;
45b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  }
46b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
47328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.orgprivate:
48b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  CriticalSectionWrapper* crit_sect_;
49b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  int count_;
50b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org};
51b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
52b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgclass CritSectTest : public ::testing::Test {
53328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.orgpublic:
543c98b828b11dd1f8c4902dfb4dabc0ea7d3a99e8andresp@webrtc.org  CritSectTest() {}
55b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
56b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // Waits a number of cycles for the count to reach a given value.
57b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // Returns true if the target is reached or passed.
58b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  bool WaitForCount(int target, ProtectedCount* count) {
59b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    int loop_counter = 0;
60b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    // On Posix, this SwitchProcess() needs to be in a loop to make the
61b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    // test both fast and non-flaky.
62b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    // With 1 us wait as the switch, up to 7 rounds have been observed.
63328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org    while (count->Count() < target && loop_counter < 100 * target) {
64b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      ++loop_counter;
65b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      SwitchProcess();
66b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    }
67b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    return (count->Count() >= target);
68b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  }
69b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org};
70b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
71b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgbool LockUnlockThenStopRunFunction(void* obj) {
72328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org  ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
73b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  the_count->Increment();
74b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  return false;
75b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
76b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
77c9faf10f99da606e2d58b0b4a79c03c232b7c50fandresp@webrtc.orgTEST_F(CritSectTest, ThreadWakesOnce) NO_THREAD_SAFETY_ANALYSIS {
78328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org  CriticalSectionWrapper* crit_sect =
79328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org      CriticalSectionWrapper::CreateCriticalSection();
80b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ProtectedCount count(crit_sect);
81b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ThreadWrapper* thread = ThreadWrapper::CreateThread(
82b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      &LockUnlockThenStopRunFunction, &count);
83b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  unsigned int id = 42;
84b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  crit_sect->Enter();
85b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ASSERT_TRUE(thread->Start(id));
86b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  SwitchProcess();
87b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // The critical section is of reentrant mode, so this should not release
88b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // the lock, even though count.Count() locks and unlocks the critical section
89b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // again.
90b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // Thus, the thread should not be able to increment the count
91b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ASSERT_EQ(0, count.Count());
92b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  crit_sect->Leave();  // This frees the thread to act.
93b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  EXPECT_TRUE(WaitForCount(1, &count));
94b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  EXPECT_TRUE(thread->Stop());
95b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  delete thread;
96b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  delete crit_sect;
97b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
98b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
99b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgbool LockUnlockRunFunction(void* obj) {
100328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org  ProtectedCount* the_count = static_cast<ProtectedCount*>(obj);
101b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  the_count->Increment();
102b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  SwitchProcess();
103b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  return true;
104b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
105b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
106c9faf10f99da606e2d58b0b4a79c03c232b7c50fandresp@webrtc.orgTEST_F(CritSectTest, ThreadWakesTwice) NO_THREAD_SAFETY_ANALYSIS {
107328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org  CriticalSectionWrapper* crit_sect =
108328820fff5a529c1ba7767debce6c396d5063499phoglund@webrtc.org      CriticalSectionWrapper::CreateCriticalSection();
109b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ProtectedCount count(crit_sect);
110b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ThreadWrapper* thread = ThreadWrapper::CreateThread(&LockUnlockRunFunction,
111b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org                                                      &count);
112b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  unsigned int id = 42;
113b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  crit_sect->Enter();  // Make sure counter stays 0 until we wait for it.
114b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ASSERT_TRUE(thread->Start(id));
115b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  crit_sect->Leave();
116b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
117b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // The thread is capable of grabbing the lock multiple times,
118b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // incrementing counter once each time.
119b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // It's possible for the count to be incremented by more than 2.
120b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  EXPECT_TRUE(WaitForCount(2, &count));
121b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  EXPECT_LE(2, count.Count());
122b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
123b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // The thread does not increment while lock is held.
124b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  crit_sect->Enter();
125b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  int count_before = count.Count();
126b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  for (int i = 0; i < 10; i++) {
127b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    SwitchProcess();
128b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  }
129b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  EXPECT_EQ(count_before, count.Count());
130b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  crit_sect->Leave();
131b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
132b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  thread->SetNotAlive();  // Tell thread to exit once run function finishes.
133b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  SwitchProcess();
1345d9a1bce7875ac9deb71e0fd2f35b71bf79e9d11hta@webrtc.org  EXPECT_TRUE(WaitForCount(count_before + 1, &count));
135b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  EXPECT_TRUE(thread->Stop());
136b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  delete thread;
137b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  delete crit_sect;
138b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
139b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
140b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}  // anonymous namespace
141b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
142b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}  // namespace webrtc
143