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:
SharedExclusiveTask(SharedExclusiveLock * shared_exclusive_lock,int * value,bool * done)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
waiting_time_in_ms() const59 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:
ReadTask(SharedExclusiveLock * shared_exclusive_lock,int * value,bool * done)71 ReadTask(SharedExclusiveLock* shared_exclusive_lock, int* value, bool* done)
72 : SharedExclusiveTask(shared_exclusive_lock, value, done) {
73 }
74
PostRead(int * value)75 void PostRead(int* value) {
76 worker_thread_->Post(this, kMsgRead, new TypedMessageData<int*>(value));
77 }
78
79 private:
OnMessage(Message * message)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:
WriteTask(SharedExclusiveLock * shared_exclusive_lock,int * value,bool * done)104 WriteTask(SharedExclusiveLock* shared_exclusive_lock, int* value, bool* done)
105 : SharedExclusiveTask(shared_exclusive_lock, value, done) {
106 }
107
PostWrite(int value)108 void PostWrite(int value) {
109 worker_thread_->Post(this, kMsgWrite, new TypedMessageData<int>(value));
110 }
111
112 private:
OnMessage(Message * message)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:
SharedExclusiveLockTest()139 SharedExclusiveLockTest() : value_(0) {
140 }
141
SetUp()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
TEST_F(SharedExclusiveLockTest,TestSharedShared)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
TEST_F(SharedExclusiveLockTest,TestSharedExclusive)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
TEST_F(SharedExclusiveLockTest,TestExclusiveShared)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
TEST_F(SharedExclusiveLockTest,TestExclusiveExclusive)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