
This test goes into an eternal loop sometimes when run under TSan v2, therefore I'm disabling it until this is resolved. Doing that, the TSan v2 bot in our new WebRTC FYI waterfall at http://build.chromium.org/p/client.webrtc.fyi/waterfall will hopefully go green. The define used only exists for TSan v2, so v1 should be unaffected. The suppression added in http://review.webrtc.org/2126005/ turns out actually has no effect, so I'm removing it again. It's probably useless since TSan v2 obviously alters the code's behavior, causing these errors. Since the errors are not TSan v2 failures, the suppression has no effect. BUG=2259 TEST=test passing locally when run as described in 2259 (since no tests are run). R=tommi@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2115005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4639 4adac7df-926f-26a2-2b94-8c16560cd09d
203 lines
6.4 KiB
C++
203 lines
6.4 KiB
C++
/*
|
|
* 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/interface/condition_variable_wrapper.h"
|
|
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
|
#include "webrtc/system_wrappers/interface/thread_wrapper.h"
|
|
#include "webrtc/system_wrappers/interface/trace.h"
|
|
#include "webrtc/system_wrappers/source/unittest_utilities.h"
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
|
|
const int kLogTrace = false; // Set to true to enable debug logging to stdout.
|
|
const int kLongWaitMs = 100 * 1000; // A long time in testing terms
|
|
const int kShortWaitMs = 2 * 1000; // Long enough for process switches to happen
|
|
|
|
// 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()
|
|
: trace_(kLogTrace) {
|
|
}
|
|
|
|
virtual void SetUp() {
|
|
thread_ = ThreadWrapper::CreateThread(&WaitingRunFunction,
|
|
&baton_);
|
|
unsigned int id = 42;
|
|
ASSERT_TRUE(thread_->Start(id));
|
|
}
|
|
|
|
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));
|
|
thread_->SetNotAlive();
|
|
ASSERT_TRUE(baton_.Grab(kShortWaitMs));
|
|
ASSERT_TRUE(thread_->Stop());
|
|
delete thread_;
|
|
}
|
|
|
|
protected:
|
|
Baton baton_;
|
|
|
|
private:
|
|
ScopedTracing trace_;
|
|
ThreadWrapper* thread_;
|
|
};
|
|
|
|
// Disable for TSan v2, see
|
|
// https://code.google.com/p/webrtc/issues/detail?id=2259 for details.
|
|
#if !defined(THREAD_SANITIZER)
|
|
|
|
// The SetUp and TearDown functions use condition variables.
|
|
// This test verifies those pieces in isolation.
|
|
TEST_F(CondVarTest, 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, 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());
|
|
}
|
|
|
|
#endif // if !defined(THREAD_SANITIZER)
|
|
|
|
} // anonymous namespace
|
|
|
|
} // namespace webrtc
|