blob: 08491fe5f5efc415ea06a085af575b56bb1e4600 [file] [log] [blame]
hta@webrtc.org72e3a892012-06-19 13:49:48 +00001/*
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 "system_wrappers/interface/condition_variable_wrapper.h"
12
13#include "gtest/gtest.h"
14#include "system_wrappers/interface/critical_section_wrapper.h"
15#include "system_wrappers/interface/thread_wrapper.h"
16#include "system_wrappers/interface/trace.h"
17#include "system_wrappers/source/unittest_utilities.h"
18
19namespace webrtc {
20
21namespace {
22
23const int kLogTrace = false; // Set to true to enable debug logging to stdout.
phoglund@webrtc.orga36d75a2012-11-14 09:55:04 +000024const int kLongWaitMs = 100 * 1000; // A long time in testing terms
25const int kShortWaitMs = 2 * 1000; // Long enough for process switches to happen
hta@webrtc.org72e3a892012-06-19 13:49:48 +000026
hta@webrtc.org72e3a892012-06-19 13:49:48 +000027// A Baton is one possible control structure one can build using
28// conditional variables.
29// A Baton is always held by one and only one active thread - unlike
30// a lock, it can never be free.
31// One can pass it or grab it - both calls have timeouts.
32// Note - a production tool would guard against passing it without
33// grabbing it first. This one is for testing, so it doesn't.
34class Baton {
35 public:
36 Baton()
37 : giver_sect_(CriticalSectionWrapper::CreateCriticalSection()),
38 crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
39 cond_var_(ConditionVariableWrapper::CreateConditionVariable()),
40 being_passed_(false),
41 pass_count_(0) {
42 }
43
44 ~Baton() {
45 delete giver_sect_;
46 delete crit_sect_;
47 delete cond_var_;
48 }
49
50 // Pass the baton. Returns false if baton is not picked up in |max_msecs|.
51 // Only one process can pass at the same time; this property is
52 // ensured by the |giver_sect_| lock.
pbos@webrtc.org046deb92013-04-09 09:06:11 +000053 bool Pass(uint32_t max_msecs) {
hta@webrtc.org72e3a892012-06-19 13:49:48 +000054 CriticalSectionScoped cs_giver(giver_sect_);
hta@webrtc.org72e3a892012-06-19 13:49:48 +000055 CriticalSectionScoped cs(crit_sect_);
56 SignalBatonAvailable();
57 const bool result = TakeBatonIfStillFree(max_msecs);
58 if (result) {
59 ++pass_count_;
hta@webrtc.org72e3a892012-06-19 13:49:48 +000060 }
61 return result;
62 }
63
64 // Grab the baton. Returns false if baton is not passed.
pbos@webrtc.org046deb92013-04-09 09:06:11 +000065 bool Grab(uint32_t max_msecs) {
hta@webrtc.org72e3a892012-06-19 13:49:48 +000066 CriticalSectionScoped cs(crit_sect_);
67 return WaitUntilBatonOffered(max_msecs);
68 }
69
70 int PassCount() {
71 // We don't allow polling PassCount() during a Pass()-call since there is
72 // no guarantee that |pass_count_| is incremented until the Pass()-call
73 // finishes. I.e. the Grab()-call may finish before |pass_count_| has been
74 // incremented.
75 // Thus, this function waits on giver_sect_.
76 CriticalSectionScoped cs(giver_sect_);
77 return pass_count_;
78 }
79
80 private:
81 // Wait/Signal forms a classical semaphore on |being_passed_|.
82 // These functions must be called with crit_sect_ held.
83 bool WaitUntilBatonOffered(int timeout_ms) {
84 while (!being_passed_) {
hta@webrtc.org72e3a892012-06-19 13:49:48 +000085 if (!cond_var_->SleepCS(*crit_sect_, timeout_ms)) {
hta@webrtc.org72e3a892012-06-19 13:49:48 +000086 return false;
87 }
88 }
89 being_passed_ = false;
90 cond_var_->Wake();
91 return true;
92 }
93
94 void SignalBatonAvailable() {
95 assert(!being_passed_);
96 being_passed_ = true;
hta@webrtc.org72e3a892012-06-19 13:49:48 +000097 cond_var_->Wake();
98 }
99
100 // Timeout extension: Wait for a limited time for someone else to
101 // take it, and take it if it's not taken.
102 // Returns true if resource is taken by someone else, false
103 // if it is taken back by the caller.
104 // This function must be called with both |giver_sect_| and
105 // |crit_sect_| held.
106 bool TakeBatonIfStillFree(int timeout_ms) {
107 bool not_timeout = true;
108 while (being_passed_ && not_timeout) {
hta@webrtc.org72e3a892012-06-19 13:49:48 +0000109 not_timeout = cond_var_->SleepCS(*crit_sect_, timeout_ms);
110 // If we're woken up while variable is still held, we may have
111 // gotten a wakeup destined for a grabber thread.
112 // This situation is not treated specially here.
113 }
114 if (!being_passed_) {
115 return true;
116 } else {
hta@webrtc.org72e3a892012-06-19 13:49:48 +0000117 assert(!not_timeout);
118 being_passed_ = false;
119 return false;
120 }
121 }
122
123 // Lock that ensures that there is only one thread in the active
124 // part of Pass() at a time.
125 // |giver_sect_| must always be acquired before |cond_var_|.
126 CriticalSectionWrapper* giver_sect_;
127 // Lock that protects |being_passed_|.
128 CriticalSectionWrapper* crit_sect_;
129 ConditionVariableWrapper* cond_var_;
130 bool being_passed_;
131 // Statistics information: Number of successfull passes.
132 int pass_count_;
133};
134
135// Function that waits on a Baton, and passes it right back.
136// We expect these calls never to time out.
137bool WaitingRunFunction(void* obj) {
andrew@webrtc.org50419b02012-11-14 19:07:54 +0000138 Baton* the_baton = static_cast<Baton*> (obj);
hta@webrtc.org72e3a892012-06-19 13:49:48 +0000139 EXPECT_TRUE(the_baton->Grab(kLongWaitMs));
hta@webrtc.org72e3a892012-06-19 13:49:48 +0000140 EXPECT_TRUE(the_baton->Pass(kLongWaitMs));
141 return true;
142}
143
144class CondVarTest : public ::testing::Test {
145 public:
146 CondVarTest()
147 : trace_(kLogTrace) {
148 }
149
150 virtual void SetUp() {
151 thread_ = ThreadWrapper::CreateThread(&WaitingRunFunction,
152 &baton_);
153 unsigned int id = 42;
154 ASSERT_TRUE(thread_->Start(id));
155 }
156
157 virtual void TearDown() {
158 // We have to wake the thread in order to make it obey the stop order.
159 // But we don't know if the thread has completed the run function, so
160 // we don't know if it will exit before or after the Pass.
161 // Thus, we need to pin it down inside its Run function (between Grab
162 // and Pass).
163 ASSERT_TRUE(baton_.Pass(kShortWaitMs));
164 thread_->SetNotAlive();
165 ASSERT_TRUE(baton_.Grab(kShortWaitMs));
166 ASSERT_TRUE(thread_->Stop());
167 delete thread_;
168 }
169
170 protected:
171 Baton baton_;
172
173 private:
174 ScopedTracing trace_;
175 ThreadWrapper* thread_;
176};
177
178// The SetUp and TearDown functions use condition variables.
179// This test verifies those pieces in isolation.
180TEST_F(CondVarTest, InitFunctionsWork) {
181 // All relevant asserts are in the SetUp and TearDown functions.
182}
183
184// This test verifies that one can use the baton multiple times.
185TEST_F(CondVarTest, PassBatonMultipleTimes) {
186 const int kNumberOfRounds = 2;
187 for (int i = 0; i < kNumberOfRounds; ++i) {
188 ASSERT_TRUE(baton_.Pass(kShortWaitMs));
189 ASSERT_TRUE(baton_.Grab(kShortWaitMs));
190 }
phoglund@webrtc.orga36d75a2012-11-14 09:55:04 +0000191 EXPECT_EQ(2 * kNumberOfRounds, baton_.PassCount());
hta@webrtc.org72e3a892012-06-19 13:49:48 +0000192}
193
194} // anonymous namespace
195
196} // namespace webrtc