1/* 2 * libjingle 3 * Copyright 2011, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "talk/base/common.h" 29#include "talk/base/gunit.h" 30#include "talk/base/messagehandler.h" 31#include "talk/base/messagequeue.h" 32#include "talk/base/scoped_ptr.h" 33#include "talk/base/sharedexclusivelock.h" 34#include "talk/base/thread.h" 35#include "talk/base/timeutils.h" 36 37namespace talk_base { 38 39static const uint32 kMsgRead = 0; 40static const uint32 kMsgWrite = 0; 41static const int kNoWaitThresholdInMs = 10; 42static const int kWaitThresholdInMs = 80; 43static const int kProcessTimeInMs = 100; 44static const int kProcessTimeoutInMs = 5000; 45 46class SharedExclusiveTask : public MessageHandler { 47 public: 48 SharedExclusiveTask(SharedExclusiveLock* shared_exclusive_lock, 49 int* value, 50 bool* done) 51 : shared_exclusive_lock_(shared_exclusive_lock), 52 waiting_time_in_ms_(0), 53 value_(value), 54 done_(done) { 55 worker_thread_.reset(new Thread()); 56 worker_thread_->Start(); 57 } 58 59 int waiting_time_in_ms() const { return waiting_time_in_ms_; } 60 61 protected: 62 scoped_ptr<Thread> worker_thread_; 63 SharedExclusiveLock* shared_exclusive_lock_; 64 int waiting_time_in_ms_; 65 int* value_; 66 bool* done_; 67}; 68 69class ReadTask : public SharedExclusiveTask { 70 public: 71 ReadTask(SharedExclusiveLock* shared_exclusive_lock, int* value, bool* done) 72 : SharedExclusiveTask(shared_exclusive_lock, value, done) { 73 } 74 75 void PostRead(int* value) { 76 worker_thread_->Post(this, kMsgRead, new TypedMessageData<int*>(value)); 77 } 78 79 private: 80 virtual void OnMessage(Message* message) { 81 ASSERT(talk_base::Thread::Current() == worker_thread_.get()); 82 ASSERT(message != NULL); 83 ASSERT(message->message_id == kMsgRead); 84 85 TypedMessageData<int*>* message_data = 86 static_cast<TypedMessageData<int*>*>(message->pdata); 87 88 uint32 start_time = Time(); 89 { 90 SharedScope ss(shared_exclusive_lock_); 91 waiting_time_in_ms_ = TimeDiff(Time(), start_time); 92 93 Thread::SleepMs(kProcessTimeInMs); 94 *message_data->data() = *value_; 95 *done_ = true; 96 } 97 delete message->pdata; 98 message->pdata = NULL; 99 } 100}; 101 102class WriteTask : public SharedExclusiveTask { 103 public: 104 WriteTask(SharedExclusiveLock* shared_exclusive_lock, int* value, bool* done) 105 : SharedExclusiveTask(shared_exclusive_lock, value, done) { 106 } 107 108 void PostWrite(int value) { 109 worker_thread_->Post(this, kMsgWrite, new TypedMessageData<int>(value)); 110 } 111 112 private: 113 virtual void OnMessage(Message* message) { 114 ASSERT(talk_base::Thread::Current() == worker_thread_.get()); 115 ASSERT(message != NULL); 116 ASSERT(message->message_id == kMsgWrite); 117 118 TypedMessageData<int>* message_data = 119 static_cast<TypedMessageData<int>*>(message->pdata); 120 121 uint32 start_time = Time(); 122 { 123 ExclusiveScope es(shared_exclusive_lock_); 124 waiting_time_in_ms_ = TimeDiff(Time(), start_time); 125 126 Thread::SleepMs(kProcessTimeInMs); 127 *value_ = message_data->data(); 128 *done_ = true; 129 } 130 delete message->pdata; 131 message->pdata = NULL; 132 } 133}; 134 135// Unit test for SharedExclusiveLock. 136class SharedExclusiveLockTest 137 : public testing::Test { 138 public: 139 SharedExclusiveLockTest() : value_(0) { 140 } 141 142 virtual void SetUp() { 143 shared_exclusive_lock_.reset(new SharedExclusiveLock()); 144 } 145 146 protected: 147 scoped_ptr<SharedExclusiveLock> shared_exclusive_lock_; 148 int value_; 149}; 150 151// Flaky: https://code.google.com/p/webrtc/issues/detail?id=3318 152TEST_F(SharedExclusiveLockTest, DISABLED_TestSharedShared) { 153 int value0, value1; 154 bool done0, done1; 155 ReadTask reader0(shared_exclusive_lock_.get(), &value_, &done0); 156 ReadTask reader1(shared_exclusive_lock_.get(), &value_, &done1); 157 158 // Test shared locks can be shared without waiting. 159 { 160 SharedScope ss(shared_exclusive_lock_.get()); 161 value_ = 1; 162 done0 = false; 163 done1 = false; 164 reader0.PostRead(&value0); 165 reader1.PostRead(&value1); 166 Thread::SleepMs(kProcessTimeInMs); 167 } 168 169 EXPECT_TRUE_WAIT(done0, kProcessTimeoutInMs); 170 EXPECT_EQ(1, value0); 171 EXPECT_LE(reader0.waiting_time_in_ms(), kNoWaitThresholdInMs); 172 EXPECT_TRUE_WAIT(done1, kProcessTimeoutInMs); 173 EXPECT_EQ(1, value1); 174 EXPECT_LE(reader1.waiting_time_in_ms(), kNoWaitThresholdInMs); 175} 176 177TEST_F(SharedExclusiveLockTest, TestSharedExclusive) { 178 bool done; 179 WriteTask writer(shared_exclusive_lock_.get(), &value_, &done); 180 181 // Test exclusive lock needs to wait for shared lock. 182 { 183 SharedScope ss(shared_exclusive_lock_.get()); 184 value_ = 1; 185 done = false; 186 writer.PostWrite(2); 187 Thread::SleepMs(kProcessTimeInMs); 188 EXPECT_EQ(1, value_); 189 } 190 191 EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); 192 EXPECT_EQ(2, value_); 193 EXPECT_GE(writer.waiting_time_in_ms(), kWaitThresholdInMs); 194} 195 196TEST_F(SharedExclusiveLockTest, TestExclusiveShared) { 197 int value; 198 bool done; 199 ReadTask reader(shared_exclusive_lock_.get(), &value_, &done); 200 201 // Test shared lock needs to wait for exclusive lock. 202 { 203 ExclusiveScope es(shared_exclusive_lock_.get()); 204 value_ = 1; 205 done = false; 206 reader.PostRead(&value); 207 Thread::SleepMs(kProcessTimeInMs); 208 value_ = 2; 209 } 210 211 EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); 212 EXPECT_EQ(2, value); 213 EXPECT_GE(reader.waiting_time_in_ms(), kWaitThresholdInMs); 214} 215 216TEST_F(SharedExclusiveLockTest, TestExclusiveExclusive) { 217 bool done; 218 WriteTask writer(shared_exclusive_lock_.get(), &value_, &done); 219 220 // Test exclusive lock needs to wait for exclusive lock. 221 { 222 ExclusiveScope es(shared_exclusive_lock_.get()); 223 value_ = 1; 224 done = false; 225 writer.PostWrite(2); 226 Thread::SleepMs(kProcessTimeInMs); 227 EXPECT_EQ(1, value_); 228 } 229 230 EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); 231 EXPECT_EQ(2, value_); 232 EXPECT_GE(writer.waiting_time_in_ms(), kWaitThresholdInMs); 233} 234 235} // namespace talk_base 236