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