Home | History | Annotate | Download | only in base
      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