| /* |
| * 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" |
| |
| 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 |
| |
| // 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() {} |
| |
| virtual void SetUp() { |
| thread_ = ThreadWrapper::CreateThread(&WaitingRunFunction, |
| &baton_, "CondVarTest"); |
| ASSERT_TRUE(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)); |
| ASSERT_TRUE(thread_->Stop()); |
| } |
| |
| protected: |
| Baton baton_; |
| |
| private: |
| rtc::scoped_ptr<ThreadWrapper> 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()); |
| } |
| |
| } // anonymous namespace |
| |
| } // namespace webrtc |