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
19namespace webrtc {
20
21namespace {
22
23const int kLogTrace = false;  // Set to true to enable debug logging to stdout.
24const int kLongWaitMs = 100*1000;  // A long time in testing terms
25const 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.
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) {
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
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  }
203  EXPECT_EQ(2*kNumberOfRounds, baton_.PassCount());
204}
205
206}  // anonymous namespace
207
208}  // namespace webrtc
209