Note: We no longer publish the latest version of our code here. We primarily use a kumc-bmi github organization. The heron ETL repository, in particular, is not public. Peers in the informatics community should see MultiSiteDev for details on requesting access.

source: webrtc/webrtc/system_wrappers/source/condition_variable_unittest.cc @ 0:4bda6873e34c

pub_scrub_3792 tip
Last change on this file since 0:4bda6873e34c was 0:4bda6873e34c, checked in by Michael Prittie <mprittie@…>, 6 years ago

Scrubbed password for publication.

File size: 6.2 KB
Line 
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 "webrtc/system_wrappers/interface/condition_variable_wrapper.h"
12
13#include "testing/gtest/include/gtest/gtest.h"
14#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
15#include "webrtc/system_wrappers/interface/thread_wrapper.h"
16#include "webrtc/system_wrappers/interface/trace.h"
17#include "webrtc/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// 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.
53  bool Pass(uint32_t max_msecs) {
54    CriticalSectionScoped cs_giver(giver_sect_);
55    CriticalSectionScoped cs(crit_sect_);
56    SignalBatonAvailable();
57    const bool result = TakeBatonIfStillFree(max_msecs);
58    if (result) {
59      ++pass_count_;
60    }
61    return result;
62  }
63
64  // Grab the baton. Returns false if baton is not passed.
65  bool Grab(uint32_t max_msecs) {
66    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_) {
85      if (!cond_var_->SleepCS(*crit_sect_, timeout_ms)) {
86        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;
97    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) {
109      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 {
117      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) {
138  Baton* the_baton = static_cast<Baton*> (obj);
139  EXPECT_TRUE(the_baton->Grab(kLongWaitMs));
140  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  }
191  EXPECT_EQ(2 * kNumberOfRounds, baton_.PassCount());
192}
193
194}  // anonymous namespace
195
196}  // namespace webrtc
Note: See TracBrowser for help on using the repository browser.