blob: 25a4683aab98f5f6c6bdfabf3c2af752a0c79759 [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
27#define LOG(...) WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, __VA_ARGS__);
28
29// A Baton is one possible control structure one can build using
30// conditional variables.
31// A Baton is always held by one and only one active thread - unlike
32// a lock, it can never be free.
33// One can pass it or grab it - both calls have timeouts.
34// Note - a production tool would guard against passing it without
35// grabbing it first. This one is for testing, so it doesn't.
36class Baton {
37 public:
38 Baton()
39 : giver_sect_(CriticalSectionWrapper::CreateCriticalSection()),
40 crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
41 cond_var_(ConditionVariableWrapper::CreateConditionVariable()),
42 being_passed_(false),
43 pass_count_(0) {
44 }
45
46 ~Baton() {
47 delete giver_sect_;
48 delete crit_sect_;
49 delete cond_var_;
50 }
51
52 // Pass the baton. Returns false if baton is not picked up in |max_msecs|.
53 // Only one process can pass at the same time; this property is
54 // ensured by the |giver_sect_| lock.
55 bool Pass(WebRtc_UWord32 max_msecs) {
56 LOG("Locking giver_sect");
57 CriticalSectionScoped cs_giver(giver_sect_);
58 LOG("Locked giver_sect, locking crit_sect");
59 CriticalSectionScoped cs(crit_sect_);
60 SignalBatonAvailable();
61 const bool result = TakeBatonIfStillFree(max_msecs);
62 if (result) {
63 ++pass_count_;
64 LOG("Pass count is %d", pass_count_);
65 }
66 return result;
67 }
68
69 // Grab the baton. Returns false if baton is not passed.
70 bool Grab(WebRtc_UWord32 max_msecs) {
71 CriticalSectionScoped cs(crit_sect_);
72 return WaitUntilBatonOffered(max_msecs);
73 }
74
75 int PassCount() {
76 // We don't allow polling PassCount() during a Pass()-call since there is
77 // no guarantee that |pass_count_| is incremented until the Pass()-call
78 // finishes. I.e. the Grab()-call may finish before |pass_count_| has been
79 // incremented.
80 // Thus, this function waits on giver_sect_.
81 CriticalSectionScoped cs(giver_sect_);
82 return pass_count_;
83 }
84
85 private:
86 // Wait/Signal forms a classical semaphore on |being_passed_|.
87 // These functions must be called with crit_sect_ held.
88 bool WaitUntilBatonOffered(int timeout_ms) {
89 while (!being_passed_) {
90 LOG("Wait waiting");
91 if (!cond_var_->SleepCS(*crit_sect_, timeout_ms)) {
92 LOG("Wait timeout");
93 return false;
94 }
95 }
96 being_passed_ = false;
97 cond_var_->Wake();
98 return true;
99 }
100
101 void SignalBatonAvailable() {
102 assert(!being_passed_);
103 being_passed_ = true;
104 LOG("Signal waking");
105 cond_var_->Wake();
106 }
107
108 // Timeout extension: Wait for a limited time for someone else to
109 // take it, and take it if it's not taken.
110 // Returns true if resource is taken by someone else, false
111 // if it is taken back by the caller.
112 // This function must be called with both |giver_sect_| and
113 // |crit_sect_| held.
114 bool TakeBatonIfStillFree(int timeout_ms) {
115 bool not_timeout = true;
116 while (being_passed_ && not_timeout) {
117 LOG("Takeback waiting");
118 not_timeout = cond_var_->SleepCS(*crit_sect_, timeout_ms);
119 // If we're woken up while variable is still held, we may have
120 // gotten a wakeup destined for a grabber thread.
121 // This situation is not treated specially here.
122 }
123 if (!being_passed_) {
124 return true;
125 } else {
126 LOG("Takeback grab");
127 assert(!not_timeout);
128 being_passed_ = false;
129 return false;
130 }
131 }
132
133 // Lock that ensures that there is only one thread in the active
134 // part of Pass() at a time.
135 // |giver_sect_| must always be acquired before |cond_var_|.
136 CriticalSectionWrapper* giver_sect_;
137 // Lock that protects |being_passed_|.
138 CriticalSectionWrapper* crit_sect_;
139 ConditionVariableWrapper* cond_var_;
140 bool being_passed_;
141 // Statistics information: Number of successfull passes.
142 int pass_count_;
143};
144
145// Function that waits on a Baton, and passes it right back.
146// We expect these calls never to time out.
147bool WaitingRunFunction(void* obj) {
phoglund@webrtc.orga36d75a2012-11-14 09:55:04 +0000148 Baton* the_baton = static_cast<Baton*>(obj);
hta@webrtc.org72e3a892012-06-19 13:49:48 +0000149 LOG("Thread waiting");
150 EXPECT_TRUE(the_baton->Grab(kLongWaitMs));
151 LOG("Thread waking parent");
152 EXPECT_TRUE(the_baton->Pass(kLongWaitMs));
153 return true;
154}
155
156class CondVarTest : public ::testing::Test {
157 public:
158 CondVarTest()
159 : trace_(kLogTrace) {
160 }
161
162 virtual void SetUp() {
163 thread_ = ThreadWrapper::CreateThread(&WaitingRunFunction,
164 &baton_);
165 unsigned int id = 42;
166 ASSERT_TRUE(thread_->Start(id));
167 }
168
169 virtual void TearDown() {
170 // We have to wake the thread in order to make it obey the stop order.
171 // But we don't know if the thread has completed the run function, so
172 // we don't know if it will exit before or after the Pass.
173 // Thus, we need to pin it down inside its Run function (between Grab
174 // and Pass).
175 ASSERT_TRUE(baton_.Pass(kShortWaitMs));
176 thread_->SetNotAlive();
177 ASSERT_TRUE(baton_.Grab(kShortWaitMs));
178 ASSERT_TRUE(thread_->Stop());
179 delete thread_;
180 }
181
182 protected:
183 Baton baton_;
184
185 private:
186 ScopedTracing trace_;
187 ThreadWrapper* thread_;
188};
189
190// The SetUp and TearDown functions use condition variables.
191// This test verifies those pieces in isolation.
192TEST_F(CondVarTest, InitFunctionsWork) {
193 // All relevant asserts are in the SetUp and TearDown functions.
194}
195
196// This test verifies that one can use the baton multiple times.
197TEST_F(CondVarTest, PassBatonMultipleTimes) {
198 const int kNumberOfRounds = 2;
199 for (int i = 0; i < kNumberOfRounds; ++i) {
200 ASSERT_TRUE(baton_.Pass(kShortWaitMs));
201 ASSERT_TRUE(baton_.Grab(kShortWaitMs));
202 }
phoglund@webrtc.orga36d75a2012-11-14 09:55:04 +0000203 EXPECT_EQ(2 * kNumberOfRounds, baton_.PassCount());
hta@webrtc.org72e3a892012-06-19 13:49:48 +0000204}
205
206} // anonymous namespace
207
208} // namespace webrtc