/* * Copyright 2011 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 #include "webrtc/base/common.h" #include "webrtc/base/gunit.h" #include "webrtc/base/messagehandler.h" #include "webrtc/base/messagequeue.h" #include "webrtc/base/sharedexclusivelock.h" #include "webrtc/base/thread.h" #include "webrtc/base/timeutils.h" #if defined(MEMORY_SANITIZER) // Flaky under MemorySanitizer, see // https://bugs.chromium.org/p/webrtc/issues/detail?id=5824 #define MAYBE_TestSharedExclusive DISABLED_TestSharedExclusive #define MAYBE_TestExclusiveExclusive DISABLED_TestExclusiveExclusive #else #define MAYBE_TestSharedExclusive TestSharedExclusive #define MAYBE_TestExclusiveExclusive TestExclusiveExclusive #endif namespace rtc { static const uint32_t kMsgRead = 0; static const uint32_t kMsgWrite = 0; static const int kNoWaitThresholdInMs = 10; static const int kWaitThresholdInMs = 80; static const int kProcessTimeInMs = 100; static const int kProcessTimeoutInMs = 5000; class SharedExclusiveTask : public MessageHandler { public: SharedExclusiveTask(SharedExclusiveLock* shared_exclusive_lock, int* value, bool* done) : shared_exclusive_lock_(shared_exclusive_lock), waiting_time_in_ms_(0), value_(value), done_(done) { worker_thread_.reset(new Thread()); worker_thread_->Start(); } int64_t waiting_time_in_ms() const { return waiting_time_in_ms_; } protected: std::unique_ptr worker_thread_; SharedExclusiveLock* shared_exclusive_lock_; int64_t waiting_time_in_ms_; int* value_; bool* done_; }; class ReadTask : public SharedExclusiveTask { public: ReadTask(SharedExclusiveLock* shared_exclusive_lock, int* value, bool* done) : SharedExclusiveTask(shared_exclusive_lock, value, done) { } void PostRead(int* value) { worker_thread_->Post(RTC_FROM_HERE, this, kMsgRead, new TypedMessageData(value)); } private: virtual void OnMessage(Message* message) { ASSERT(rtc::Thread::Current() == worker_thread_.get()); ASSERT(message != NULL); ASSERT(message->message_id == kMsgRead); TypedMessageData* message_data = static_cast*>(message->pdata); int64_t start_time = TimeMillis(); { SharedScope ss(shared_exclusive_lock_); waiting_time_in_ms_ = TimeDiff(TimeMillis(), start_time); Thread::SleepMs(kProcessTimeInMs); *message_data->data() = *value_; *done_ = true; } delete message->pdata; message->pdata = NULL; } }; class WriteTask : public SharedExclusiveTask { public: WriteTask(SharedExclusiveLock* shared_exclusive_lock, int* value, bool* done) : SharedExclusiveTask(shared_exclusive_lock, value, done) { } void PostWrite(int value) { worker_thread_->Post(RTC_FROM_HERE, this, kMsgWrite, new TypedMessageData(value)); } private: virtual void OnMessage(Message* message) { ASSERT(rtc::Thread::Current() == worker_thread_.get()); ASSERT(message != NULL); ASSERT(message->message_id == kMsgWrite); TypedMessageData* message_data = static_cast*>(message->pdata); int64_t start_time = TimeMillis(); { ExclusiveScope es(shared_exclusive_lock_); waiting_time_in_ms_ = TimeDiff(TimeMillis(), start_time); Thread::SleepMs(kProcessTimeInMs); *value_ = message_data->data(); *done_ = true; } delete message->pdata; message->pdata = NULL; } }; // Unit test for SharedExclusiveLock. class SharedExclusiveLockTest : public testing::Test { public: SharedExclusiveLockTest() : value_(0) { } virtual void SetUp() { shared_exclusive_lock_.reset(new SharedExclusiveLock()); } protected: std::unique_ptr shared_exclusive_lock_; int value_; }; // Flaky: https://code.google.com/p/webrtc/issues/detail?id=3318 TEST_F(SharedExclusiveLockTest, TestSharedShared) { int value0, value1; bool done0, done1; ReadTask reader0(shared_exclusive_lock_.get(), &value_, &done0); ReadTask reader1(shared_exclusive_lock_.get(), &value_, &done1); // Test shared locks can be shared without waiting. { SharedScope ss(shared_exclusive_lock_.get()); value_ = 1; done0 = false; done1 = false; reader0.PostRead(&value0); reader1.PostRead(&value1); Thread::SleepMs(kProcessTimeInMs); } EXPECT_TRUE_WAIT(done0, kProcessTimeoutInMs); EXPECT_EQ(1, value0); EXPECT_LE(reader0.waiting_time_in_ms(), kNoWaitThresholdInMs); EXPECT_TRUE_WAIT(done1, kProcessTimeoutInMs); EXPECT_EQ(1, value1); EXPECT_LE(reader1.waiting_time_in_ms(), kNoWaitThresholdInMs); } TEST_F(SharedExclusiveLockTest, MAYBE_TestSharedExclusive) { bool done; WriteTask writer(shared_exclusive_lock_.get(), &value_, &done); // Test exclusive lock needs to wait for shared lock. { SharedScope ss(shared_exclusive_lock_.get()); value_ = 1; done = false; writer.PostWrite(2); Thread::SleepMs(kProcessTimeInMs); EXPECT_EQ(1, value_); } EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); EXPECT_EQ(2, value_); EXPECT_GE(writer.waiting_time_in_ms(), kWaitThresholdInMs); } TEST_F(SharedExclusiveLockTest, TestExclusiveShared) { int value; bool done; ReadTask reader(shared_exclusive_lock_.get(), &value_, &done); // Test shared lock needs to wait for exclusive lock. { ExclusiveScope es(shared_exclusive_lock_.get()); value_ = 1; done = false; reader.PostRead(&value); Thread::SleepMs(kProcessTimeInMs); value_ = 2; } EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); EXPECT_EQ(2, value); EXPECT_GE(reader.waiting_time_in_ms(), kWaitThresholdInMs); } TEST_F(SharedExclusiveLockTest, MAYBE_TestExclusiveExclusive) { bool done; WriteTask writer(shared_exclusive_lock_.get(), &value_, &done); // Test exclusive lock needs to wait for exclusive lock. { ExclusiveScope es(shared_exclusive_lock_.get()); value_ = 1; done = false; writer.PostWrite(2); Thread::SleepMs(kProcessTimeInMs); EXPECT_EQ(1, value_); } EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); EXPECT_EQ(2, value_); EXPECT_GE(writer.waiting_time_in_ms(), kWaitThresholdInMs); } } // namespace rtc