• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
19 namespace webrtc {
20 
21 namespace {
22 
23 const int kLogTrace = false;  // Set to true to enable debug logging to stdout.
24 const int kLongWaitMs = 100*1000;  // A long time in testing terms
25 const int kShortWaitMs = 2*1000;  // Long enough for process switches to happen
26 
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.
36 class Baton {
37  public:
Baton()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 
~Baton()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.
Pass(WebRtc_UWord32 max_msecs)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.
Grab(WebRtc_UWord32 max_msecs)70   bool Grab(WebRtc_UWord32 max_msecs) {
71     CriticalSectionScoped cs(crit_sect_);
72     return WaitUntilBatonOffered(max_msecs);
73   }
74 
PassCount()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.
WaitUntilBatonOffered(int timeout_ms)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 
SignalBatonAvailable()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.
TakeBatonIfStillFree(int timeout_ms)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.
WaitingRunFunction(void * obj)147 bool WaitingRunFunction(void* obj) {
148   Baton* the_baton = static_cast<Baton*> (obj);
149   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 
156 class CondVarTest : public ::testing::Test {
157  public:
CondVarTest()158   CondVarTest()
159     : trace_(kLogTrace) {
160   }
161 
SetUp()162   virtual void SetUp() {
163     thread_ = ThreadWrapper::CreateThread(&WaitingRunFunction,
164                                           &baton_);
165     unsigned int id = 42;
166     ASSERT_TRUE(thread_->Start(id));
167   }
168 
TearDown()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.
TEST_F(CondVarTest,InitFunctionsWork)192 TEST_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.
TEST_F(CondVarTest,PassBatonMultipleTimes)197 TEST_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   }
203   EXPECT_EQ(2*kNumberOfRounds, baton_.PassCount());
204 }
205 
206 }  // anonymous namespace
207 
208 }  // namespace webrtc
209