1 /* 2 * libjingle 3 * Copyright 2014, 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 <set> 29 #include <vector> 30 31 #include "talk/base/criticalsection.h" 32 #include "talk/base/event.h" 33 #include "talk/base/gunit.h" 34 #include "talk/base/scopedptrcollection.h" 35 #include "talk/base/thread.h" 36 37 namespace talk_base { 38 39 namespace { 40 41 const int kLongTime = 10000; // 10 seconds 42 const int kNumThreads = 16; 43 const int kOperationsToRun = 1000; 44 45 template <class T> 46 class AtomicOpRunner : public MessageHandler { 47 public: 48 explicit AtomicOpRunner(int initial_value) 49 : value_(initial_value), 50 threads_active_(0), 51 start_event_(true, false), 52 done_event_(true, false) {} 53 54 int value() const { return value_; } 55 56 bool Run() { 57 // Signal all threads to start. 58 start_event_.Set(); 59 60 // Wait for all threads to finish. 61 return done_event_.Wait(kLongTime); 62 } 63 64 void SetExpectedThreadCount(int count) { 65 threads_active_ = count; 66 } 67 68 virtual void OnMessage(Message* msg) { 69 std::vector<int> values; 70 values.reserve(kOperationsToRun); 71 72 // Wait to start. 73 ASSERT_TRUE(start_event_.Wait(kLongTime)); 74 75 // Generate a bunch of values by updating value_ atomically. 76 for (int i = 0; i < kOperationsToRun; ++i) { 77 values.push_back(T::AtomicOp(&value_)); 78 } 79 80 { // Add them all to the set. 81 CritScope cs(&all_values_crit_); 82 for (size_t i = 0; i < values.size(); ++i) { 83 std::pair<std::set<int>::iterator, bool> result = 84 all_values_.insert(values[i]); 85 // Each value should only be taken by one thread, so if this value 86 // has already been added, something went wrong. 87 EXPECT_TRUE(result.second) 88 << "Thread=" << Thread::Current() << " value=" << values[i]; 89 } 90 } 91 92 // Signal that we're done. 93 if (AtomicOps::Decrement(&threads_active_) == 0) { 94 done_event_.Set(); 95 } 96 } 97 98 private: 99 int value_; 100 int threads_active_; 101 CriticalSection all_values_crit_; 102 std::set<int> all_values_; 103 Event start_event_; 104 Event done_event_; 105 }; 106 107 struct IncrementOp { 108 static int AtomicOp(int* i) { return AtomicOps::Increment(i); } 109 }; 110 111 struct DecrementOp { 112 static int AtomicOp(int* i) { return AtomicOps::Decrement(i); } 113 }; 114 115 void StartThreads(ScopedPtrCollection<Thread>* threads, 116 MessageHandler* handler) { 117 for (int i = 0; i < kNumThreads; ++i) { 118 Thread* thread = new Thread(); 119 thread->Start(); 120 thread->Post(handler); 121 threads->PushBack(thread); 122 } 123 } 124 125 } // namespace 126 127 TEST(AtomicOpsTest, Simple) { 128 int value = 0; 129 EXPECT_EQ(1, AtomicOps::Increment(&value)); 130 EXPECT_EQ(1, value); 131 EXPECT_EQ(2, AtomicOps::Increment(&value)); 132 EXPECT_EQ(2, value); 133 EXPECT_EQ(1, AtomicOps::Decrement(&value)); 134 EXPECT_EQ(1, value); 135 EXPECT_EQ(0, AtomicOps::Decrement(&value)); 136 EXPECT_EQ(0, value); 137 } 138 139 TEST(AtomicOpsTest, Increment) { 140 // Create and start lots of threads. 141 AtomicOpRunner<IncrementOp> runner(0); 142 ScopedPtrCollection<Thread> threads; 143 StartThreads(&threads, &runner); 144 runner.SetExpectedThreadCount(kNumThreads); 145 146 // Release the hounds! 147 EXPECT_TRUE(runner.Run()); 148 EXPECT_EQ(kOperationsToRun * kNumThreads, runner.value()); 149 } 150 151 TEST(AtomicOpsTest, Decrement) { 152 // Create and start lots of threads. 153 AtomicOpRunner<DecrementOp> runner(kOperationsToRun * kNumThreads); 154 ScopedPtrCollection<Thread> threads; 155 StartThreads(&threads, &runner); 156 runner.SetExpectedThreadCount(kNumThreads); 157 158 // Release the hounds! 159 EXPECT_TRUE(runner.Run()); 160 EXPECT_EQ(0, runner.value()); 161 } 162 163 } // namespace talk_base 164