blob: aad55ac611b7b70aacdc700439ba83712617d1cb [file] [log] [blame]
* 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 {
: giver_sect_(CriticalSectionWrapper::CreateCriticalSection()),
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_);
const bool result = TakeBatonIfStillFree(max_msecs);
if (result) {
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_;
// 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;
return true;
void SignalBatonAvailable() {
being_passed_ = true;
// 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 {
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);
return true;
class CondVarTest : public ::testing::Test {
CondVarTest() {}
virtual void SetUp() {
thread_ = PlatformThread::CreateThread(&WaitingRunFunction, &baton_,
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).
Baton baton_;
rtc::scoped_ptr<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) {
EXPECT_EQ(2 * kNumberOfRounds, baton_.PassCount());
TEST(CondVarWaitTest, WaitingWaits) {
rtc::scoped_ptr<CriticalSectionWrapper> crit_sect(
rtc::scoped_ptr<ConditionVariableWrapper> cond_var(
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