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 37 namespace talk_base { 38 39 static const uint32 kMsgRead = 0; 40 static const uint32 kMsgWrite = 0; 41 static const int kNoWaitThresholdInMs = 10; 42 static const int kWaitThresholdInMs = 80; 43 static const int kProcessTimeInMs = 100; 44 static const int kProcessTimeoutInMs = 5000; 45 46 class 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 69 class 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 102 class 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. 136 class 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 TEST_F(SharedExclusiveLockTest, TestSharedShared) { 152 int value0, value1; 153 bool done0, done1; 154 ReadTask reader0(shared_exclusive_lock_.get(), &value_, &done0); 155 ReadTask reader1(shared_exclusive_lock_.get(), &value_, &done1); 156 157 // Test shared locks can be shared without waiting. 158 { 159 SharedScope ss(shared_exclusive_lock_.get()); 160 value_ = 1; 161 done0 = false; 162 done1 = false; 163 reader0.PostRead(&value0); 164 reader1.PostRead(&value1); 165 Thread::SleepMs(kProcessTimeInMs); 166 } 167 168 EXPECT_TRUE_WAIT(done0, kProcessTimeoutInMs); 169 EXPECT_EQ(1, value0); 170 EXPECT_LE(reader0.waiting_time_in_ms(), kNoWaitThresholdInMs); 171 EXPECT_TRUE_WAIT(done1, kProcessTimeoutInMs); 172 EXPECT_EQ(1, value1); 173 EXPECT_LE(reader1.waiting_time_in_ms(), kNoWaitThresholdInMs); 174 } 175 176 TEST_F(SharedExclusiveLockTest, TestSharedExclusive) { 177 bool done; 178 WriteTask writer(shared_exclusive_lock_.get(), &value_, &done); 179 180 // Test exclusive lock needs to wait for shared lock. 181 { 182 SharedScope ss(shared_exclusive_lock_.get()); 183 value_ = 1; 184 done = false; 185 writer.PostWrite(2); 186 Thread::SleepMs(kProcessTimeInMs); 187 EXPECT_EQ(1, value_); 188 } 189 190 EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); 191 EXPECT_EQ(2, value_); 192 EXPECT_GE(writer.waiting_time_in_ms(), kWaitThresholdInMs); 193 } 194 195 TEST_F(SharedExclusiveLockTest, TestExclusiveShared) { 196 int value; 197 bool done; 198 ReadTask reader(shared_exclusive_lock_.get(), &value_, &done); 199 200 // Test shared lock needs to wait for exclusive lock. 201 { 202 ExclusiveScope es(shared_exclusive_lock_.get()); 203 value_ = 1; 204 done = false; 205 reader.PostRead(&value); 206 Thread::SleepMs(kProcessTimeInMs); 207 value_ = 2; 208 } 209 210 EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); 211 EXPECT_EQ(2, value); 212 EXPECT_GE(reader.waiting_time_in_ms(), kWaitThresholdInMs); 213 } 214 215 TEST_F(SharedExclusiveLockTest, TestExclusiveExclusive) { 216 bool done; 217 WriteTask writer(shared_exclusive_lock_.get(), &value_, &done); 218 219 // Test exclusive lock needs to wait for exclusive lock. 220 { 221 ExclusiveScope es(shared_exclusive_lock_.get()); 222 value_ = 1; 223 done = false; 224 writer.PostWrite(2); 225 Thread::SleepMs(kProcessTimeInMs); 226 EXPECT_EQ(1, value_); 227 } 228 229 EXPECT_TRUE_WAIT(done, kProcessTimeoutInMs); 230 EXPECT_EQ(2, value_); 231 EXPECT_GE(writer.waiting_time_in_ms(), kWaitThresholdInMs); 232 } 233 234 } // namespace talk_base 235