| /* |
| * 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 <memory> |
| |
| #include "webrtc/base/checks.h" |
| #include "webrtc/base/gunit.h" |
| #include "webrtc/base/event.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" |
| |
| 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, |
| Event* done) |
| : shared_exclusive_lock_(shared_exclusive_lock), |
| value_(value), |
| done_(done) { |
| worker_thread_.reset(new Thread()); |
| worker_thread_->Start(); |
| } |
| |
| protected: |
| std::unique_ptr<Thread> worker_thread_; |
| SharedExclusiveLock* shared_exclusive_lock_; |
| int* value_; |
| Event* done_; |
| }; |
| |
| class ReadTask : public SharedExclusiveTask { |
| public: |
| ReadTask(SharedExclusiveLock* shared_exclusive_lock, int* value, Event* done) |
| : SharedExclusiveTask(shared_exclusive_lock, value, done) { |
| } |
| |
| void PostRead(int* value) { |
| worker_thread_->Post(RTC_FROM_HERE, this, kMsgRead, |
| new TypedMessageData<int*>(value)); |
| } |
| |
| private: |
| virtual void OnMessage(Message* message) { |
| RTC_CHECK(rtc::Thread::Current() == worker_thread_.get()); |
| RTC_CHECK(message != nullptr); |
| RTC_CHECK(message->message_id == kMsgRead); |
| |
| TypedMessageData<int*>* message_data = |
| static_cast<TypedMessageData<int*>*>(message->pdata); |
| |
| { |
| SharedScope ss(shared_exclusive_lock_); |
| *message_data->data() = *value_; |
| done_->Set(); |
| } |
| delete message->pdata; |
| message->pdata = nullptr; |
| } |
| }; |
| |
| class WriteTask : public SharedExclusiveTask { |
| public: |
| WriteTask(SharedExclusiveLock* shared_exclusive_lock, int* value, Event* done) |
| : SharedExclusiveTask(shared_exclusive_lock, value, done) { |
| } |
| |
| void PostWrite(int value) { |
| worker_thread_->Post(RTC_FROM_HERE, this, kMsgWrite, |
| new TypedMessageData<int>(value)); |
| } |
| |
| private: |
| virtual void OnMessage(Message* message) { |
| RTC_CHECK(rtc::Thread::Current() == worker_thread_.get()); |
| RTC_CHECK(message != nullptr); |
| RTC_CHECK(message->message_id == kMsgWrite); |
| |
| TypedMessageData<int>* message_data = |
| static_cast<TypedMessageData<int>*>(message->pdata); |
| |
| { |
| ExclusiveScope es(shared_exclusive_lock_); |
| *value_ = message_data->data(); |
| done_->Set(); |
| } |
| delete message->pdata; |
| message->pdata = nullptr; |
| } |
| }; |
| |
| // 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<SharedExclusiveLock> shared_exclusive_lock_; |
| int value_; |
| }; |
| |
| TEST_F(SharedExclusiveLockTest, TestSharedShared) { |
| int value0, value1; |
| Event done0(false, false), done1(false, false); |
| 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; |
| reader0.PostRead(&value0); |
| reader1.PostRead(&value1); |
| |
| EXPECT_TRUE(done0.Wait(kProcessTimeoutInMs)); |
| EXPECT_TRUE(done1.Wait(kProcessTimeoutInMs)); |
| EXPECT_EQ(1, value0); |
| EXPECT_EQ(1, value1); |
| } |
| } |
| |
| TEST_F(SharedExclusiveLockTest, TestSharedExclusive) { |
| Event done(false, false); |
| 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; |
| writer.PostWrite(2); |
| EXPECT_FALSE(done.Wait(kProcessTimeInMs)); |
| } |
| EXPECT_TRUE(done.Wait(kProcessTimeoutInMs)); |
| EXPECT_EQ(2, value_); |
| } |
| |
| TEST_F(SharedExclusiveLockTest, TestExclusiveShared) { |
| int value; |
| Event done(false, false); |
| 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; |
| reader.PostRead(&value); |
| EXPECT_FALSE(done.Wait(kProcessTimeInMs)); |
| value_ = 2; |
| } |
| |
| EXPECT_TRUE(done.Wait(kProcessTimeoutInMs)); |
| EXPECT_EQ(2, value); |
| } |
| |
| TEST_F(SharedExclusiveLockTest, TestExclusiveExclusive) { |
| Event done(false, false); |
| WriteTask writer(shared_exclusive_lock_.get(), &value_, &done); |
| |
| // Test exclusive lock needs to wait for exclusive lock. |
| { |
| ExclusiveScope es(shared_exclusive_lock_.get()); |
| // Start the writer task only after holding the lock, to ensure it need |
| value_ = 1; |
| writer.PostWrite(2); |
| EXPECT_FALSE(done.Wait(kProcessTimeInMs)); |
| EXPECT_EQ(1, value_); |
| } |
| |
| EXPECT_TRUE(done.Wait(kProcessTimeoutInMs)); |
| EXPECT_EQ(2, value_); |
| } |
| |
| } // namespace rtc |