/* * 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 "system_wrappers/interface/condition_variable_wrapper.h" #include "gtest/gtest.h" #include "system_wrappers/interface/critical_section_wrapper.h" #include "system_wrappers/interface/thread_wrapper.h" #include "system_wrappers/interface/trace.h" #include "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 #define LOG(...) WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, __VA_ARGS__); // 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(WebRtc_UWord32 max_msecs) { LOG("Locking giver_sect"); CriticalSectionScoped cs_giver(giver_sect_); LOG("Locked giver_sect, locking crit_sect"); CriticalSectionScoped cs(crit_sect_); SignalBatonAvailable(); const bool result = TakeBatonIfStillFree(max_msecs); if (result) { ++pass_count_; LOG("Pass count is %d", pass_count_); } return result; } // Grab the baton. Returns false if baton is not passed. bool Grab(WebRtc_UWord32 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_) { LOG("Wait waiting"); if (!cond_var_->SleepCS(*crit_sect_, timeout_ms)) { LOG("Wait timeout"); return false; } } being_passed_ = false; cond_var_->Wake(); return true; } void SignalBatonAvailable() { assert(!being_passed_); being_passed_ = true; LOG("Signal waking"); 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) { LOG("Takeback waiting"); 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 { LOG("Takeback grab"); 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); LOG("Thread waiting"); EXPECT_TRUE(the_baton->Grab(kLongWaitMs)); LOG("Thread waking parent"); 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_; }; // 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()); } } // anonymous namespace } // namespace webrtc