aboutsummaryrefslogtreecommitdiff
path: root/webrtc/system_wrappers/source/condition_variable_unittest.cc
blob: 5a8dd0b36ea0d873953f799d1a778cda547bbb6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/system_wrappers/include/condition_variable_wrapper.h"

#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/platform_thread.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
#include "webrtc/system_wrappers/include/tick_util.h"
#include "webrtc/system_wrappers/include/trace.h"

namespace webrtc {

namespace {

const int kLongWaitMs = 100 * 1000; // A long time in testing terms
const int kShortWaitMs = 2 * 1000; // Long enough for process switches to happen
const int kVeryShortWaitMs = 20; // Used when we want a timeout

// A Baton is one possible control structure one can build using
// conditional variables.
// A Baton is always held by one and only one active thread - unlike
// a lock, it can never be free.
// One can pass it or grab it - both calls have timeouts.
// Note - a production tool would guard against passing it without
// grabbing it first. This one is for testing, so it doesn't.
class Baton {
 public:
  Baton()
    : giver_sect_(CriticalSectionWrapper::CreateCriticalSection()),
      crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
      cond_var_(ConditionVariableWrapper::CreateConditionVariable()),
      being_passed_(false),
      pass_count_(0) {
  }

  ~Baton() {
    delete giver_sect_;
    delete crit_sect_;
    delete cond_var_;
  }

  // Pass the baton. Returns false if baton is not picked up in |max_msecs|.
  // Only one process can pass at the same time; this property is
  // ensured by the |giver_sect_| lock.
  bool Pass(uint32_t max_msecs) {
    CriticalSectionScoped cs_giver(giver_sect_);
    CriticalSectionScoped cs(crit_sect_);
    SignalBatonAvailable();
    const bool result = TakeBatonIfStillFree(max_msecs);
    if (result) {
      ++pass_count_;
    }
    return result;
  }

  // Grab the baton. Returns false if baton is not passed.
  bool Grab(uint32_t max_msecs) {
    CriticalSectionScoped cs(crit_sect_);
    return WaitUntilBatonOffered(max_msecs);
  }

  int PassCount() {
    // We don't allow polling PassCount() during a Pass()-call since there is
    // no guarantee that |pass_count_| is incremented until the Pass()-call
    // finishes. I.e. the Grab()-call may finish before |pass_count_| has been
    // incremented.
    // Thus, this function waits on giver_sect_.
    CriticalSectionScoped cs(giver_sect_);
    return pass_count_;
  }

 private:
  // Wait/Signal forms a classical semaphore on |being_passed_|.
  // These functions must be called with crit_sect_ held.
  bool WaitUntilBatonOffered(int timeout_ms) {
    while (!being_passed_) {
      if (!cond_var_->SleepCS(*crit_sect_, timeout_ms)) {
        return false;
      }
    }
    being_passed_ = false;
    cond_var_->Wake();
    return true;
  }

  void SignalBatonAvailable() {
    assert(!being_passed_);
    being_passed_ = true;
    cond_var_->Wake();
  }

  // Timeout extension: Wait for a limited time for someone else to
  // take it, and take it if it's not taken.
  // Returns true if resource is taken by someone else, false
  // if it is taken back by the caller.
  // This function must be called with both |giver_sect_| and
  // |crit_sect_| held.
  bool TakeBatonIfStillFree(int timeout_ms) {
    bool not_timeout = true;
    while (being_passed_ && not_timeout) {
      not_timeout = cond_var_->SleepCS(*crit_sect_, timeout_ms);
      // If we're woken up while variable is still held, we may have
      // gotten a wakeup destined for a grabber thread.
      // This situation is not treated specially here.
    }
    if (!being_passed_) {
      return true;
    } else {
      assert(!not_timeout);
      being_passed_ = false;
      return false;
    }
  }

  // Lock that ensures that there is only one thread in the active
  // part of Pass() at a time.
  // |giver_sect_| must always be acquired before |cond_var_|.
  CriticalSectionWrapper* giver_sect_;
  // Lock that protects |being_passed_|.
  CriticalSectionWrapper* crit_sect_;
  ConditionVariableWrapper* cond_var_;
  bool being_passed_;
  // Statistics information: Number of successfull passes.
  int pass_count_;
};

// Function that waits on a Baton, and passes it right back.
// We expect these calls never to time out.
bool WaitingRunFunction(void* obj) {
  Baton* the_baton = static_cast<Baton*> (obj);
  EXPECT_TRUE(the_baton->Grab(kLongWaitMs));
  EXPECT_TRUE(the_baton->Pass(kLongWaitMs));
  return true;
}

class CondVarTest : public ::testing::Test {
 public:
  CondVarTest() : thread_(&WaitingRunFunction, &baton_, "CondVarTest") {}

  virtual void SetUp() {
    thread_.Start();
  }

  virtual void TearDown() {
    // We have to wake the thread in order to make it obey the stop order.
    // But we don't know if the thread has completed the run function, so
    // we don't know if it will exit before or after the Pass.
    // Thus, we need to pin it down inside its Run function (between Grab
    // and Pass).
    ASSERT_TRUE(baton_.Pass(kShortWaitMs));
    ASSERT_TRUE(baton_.Grab(kShortWaitMs));
    thread_.Stop();
  }

 protected:
  Baton baton_;

 private:
  rtc::PlatformThread thread_;
};

// The SetUp and TearDown functions use condition variables.
// This test verifies those pieces in isolation.
// Disabled due to flakiness.  See bug 4262 for details.
TEST_F(CondVarTest, DISABLED_InitFunctionsWork) {
  // All relevant asserts are in the SetUp and TearDown functions.
}

// This test verifies that one can use the baton multiple times.
TEST_F(CondVarTest, DISABLED_PassBatonMultipleTimes) {
  const int kNumberOfRounds = 2;
  for (int i = 0; i < kNumberOfRounds; ++i) {
    ASSERT_TRUE(baton_.Pass(kShortWaitMs));
    ASSERT_TRUE(baton_.Grab(kShortWaitMs));
  }
  EXPECT_EQ(2 * kNumberOfRounds, baton_.PassCount());
}

TEST(CondVarWaitTest, WaitingWaits) {
  rtc::scoped_ptr<CriticalSectionWrapper> crit_sect(
      CriticalSectionWrapper::CreateCriticalSection());
  rtc::scoped_ptr<ConditionVariableWrapper> cond_var(
      ConditionVariableWrapper::CreateConditionVariable());
  CriticalSectionScoped cs(crit_sect.get());
  int64_t start_ms = TickTime::MillisecondTimestamp();
  EXPECT_FALSE(cond_var->SleepCS(*(crit_sect), kVeryShortWaitMs));
  int64_t end_ms = TickTime::MillisecondTimestamp();
  EXPECT_LE(start_ms + kVeryShortWaitMs, end_ms)
      << "actual elapsed:" << end_ms - start_ms;
}

}  // anonymous namespace

}  // namespace webrtc