1 /* 2 * Copyright 2014 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <set> 12 #include <vector> 13 14 #include "webrtc/base/criticalsection.h" 15 #include "webrtc/base/event.h" 16 #include "webrtc/base/gunit.h" 17 #include "webrtc/base/scoped_ptr.h" 18 #include "webrtc/base/scopedptrcollection.h" 19 #include "webrtc/base/thread.h" 20 21 namespace rtc { 22 23 namespace { 24 25 const int kLongTime = 10000; // 10 seconds 26 const int kNumThreads = 16; 27 const int kOperationsToRun = 1000; 28 29 class UniqueValueVerifier { 30 public: 31 void Verify(const std::vector<int>& values) { 32 for (size_t i = 0; i < values.size(); ++i) { 33 std::pair<std::set<int>::iterator, bool> result = 34 all_values_.insert(values[i]); 35 // Each value should only be taken by one thread, so if this value 36 // has already been added, something went wrong. 37 EXPECT_TRUE(result.second) 38 << " Thread=" << Thread::Current() << " value=" << values[i]; 39 } 40 } 41 42 void Finalize() {} 43 44 private: 45 std::set<int> all_values_; 46 }; 47 48 class CompareAndSwapVerifier { 49 public: 50 CompareAndSwapVerifier() : zero_count_(0) {} 51 52 void Verify(const std::vector<int>& values) { 53 for (auto v : values) { 54 if (v == 0) { 55 EXPECT_EQ(0, zero_count_) << "Thread=" << Thread::Current(); 56 ++zero_count_; 57 } else { 58 EXPECT_EQ(1, v) << " Thread=" << Thread::Current(); 59 } 60 } 61 } 62 63 void Finalize() { 64 EXPECT_EQ(1, zero_count_); 65 } 66 private: 67 int zero_count_; 68 }; 69 70 class RunnerBase : public MessageHandler { 71 public: 72 explicit RunnerBase(int value) 73 : threads_active_(0), 74 start_event_(true, false), 75 done_event_(true, false), 76 shared_value_(value) {} 77 78 bool Run() { 79 // Signal all threads to start. 80 start_event_.Set(); 81 82 // Wait for all threads to finish. 83 return done_event_.Wait(kLongTime); 84 } 85 86 void SetExpectedThreadCount(int count) { 87 threads_active_ = count; 88 } 89 90 int shared_value() const { return shared_value_; } 91 92 protected: 93 // Derived classes must override OnMessage, and call BeforeStart and AfterEnd 94 // at the beginning and the end of OnMessage respectively. 95 void BeforeStart() { 96 ASSERT_TRUE(start_event_.Wait(kLongTime)); 97 } 98 99 // Returns true if all threads have finished. 100 bool AfterEnd() { 101 if (AtomicOps::Decrement(&threads_active_) == 0) { 102 done_event_.Set(); 103 return true; 104 } 105 return false; 106 } 107 108 int threads_active_; 109 Event start_event_; 110 Event done_event_; 111 int shared_value_; 112 }; 113 114 class LOCKABLE CriticalSectionLock { 115 public: 116 void Lock() EXCLUSIVE_LOCK_FUNCTION() { 117 cs_.Enter(); 118 } 119 void Unlock() UNLOCK_FUNCTION() { 120 cs_.Leave(); 121 } 122 123 private: 124 CriticalSection cs_; 125 }; 126 127 template <class Lock> 128 class LockRunner : public RunnerBase { 129 public: 130 LockRunner() : RunnerBase(0) {} 131 132 void OnMessage(Message* msg) override { 133 BeforeStart(); 134 135 lock_.Lock(); 136 137 EXPECT_EQ(0, shared_value_); 138 int old = shared_value_; 139 140 // Use a loop to increase the chance of race. 141 for (int i = 0; i < kOperationsToRun; ++i) { 142 ++shared_value_; 143 } 144 EXPECT_EQ(old + kOperationsToRun, shared_value_); 145 shared_value_ = 0; 146 147 lock_.Unlock(); 148 149 AfterEnd(); 150 } 151 152 private: 153 Lock lock_; 154 }; 155 156 template <class Op, class Verifier> 157 class AtomicOpRunner : public RunnerBase { 158 public: 159 explicit AtomicOpRunner(int initial_value) : RunnerBase(initial_value) {} 160 161 void OnMessage(Message* msg) override { 162 BeforeStart(); 163 164 std::vector<int> values; 165 values.reserve(kOperationsToRun); 166 167 // Generate a bunch of values by updating shared_value_ atomically. 168 for (int i = 0; i < kOperationsToRun; ++i) { 169 values.push_back(Op::AtomicOp(&shared_value_)); 170 } 171 172 { // Add them all to the set. 173 CritScope cs(&all_values_crit_); 174 verifier_.Verify(values); 175 } 176 177 if (AfterEnd()) { 178 verifier_.Finalize(); 179 } 180 } 181 182 private: 183 CriticalSection all_values_crit_; 184 Verifier verifier_; 185 }; 186 187 struct IncrementOp { 188 static int AtomicOp(int* i) { return AtomicOps::Increment(i); } 189 }; 190 191 struct DecrementOp { 192 static int AtomicOp(int* i) { return AtomicOps::Decrement(i); } 193 }; 194 195 struct CompareAndSwapOp { 196 static int AtomicOp(int* i) { return AtomicOps::CompareAndSwap(i, 0, 1); } 197 }; 198 199 void StartThreads(ScopedPtrCollection<Thread>* threads, 200 MessageHandler* handler) { 201 for (int i = 0; i < kNumThreads; ++i) { 202 Thread* thread = new Thread(); 203 thread->Start(); 204 thread->Post(handler); 205 threads->PushBack(thread); 206 } 207 } 208 209 } // namespace 210 211 TEST(AtomicOpsTest, Simple) { 212 int value = 0; 213 EXPECT_EQ(1, AtomicOps::Increment(&value)); 214 EXPECT_EQ(1, value); 215 EXPECT_EQ(2, AtomicOps::Increment(&value)); 216 EXPECT_EQ(2, value); 217 EXPECT_EQ(1, AtomicOps::Decrement(&value)); 218 EXPECT_EQ(1, value); 219 EXPECT_EQ(0, AtomicOps::Decrement(&value)); 220 EXPECT_EQ(0, value); 221 } 222 223 TEST(AtomicOpsTest, SimplePtr) { 224 class Foo {}; 225 Foo* volatile foo = nullptr; 226 scoped_ptr<Foo> a(new Foo()); 227 scoped_ptr<Foo> b(new Foo()); 228 // Reading the initial value should work as expected. 229 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == nullptr); 230 // Setting using compare and swap should work. 231 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr( 232 &foo, static_cast<Foo*>(nullptr), a.get()) == nullptr); 233 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get()); 234 // Setting another value but with the wrong previous pointer should fail 235 // (remain a). 236 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr( 237 &foo, static_cast<Foo*>(nullptr), b.get()) == a.get()); 238 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get()); 239 // Replacing a with b should work. 240 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(&foo, a.get(), b.get()) == 241 a.get()); 242 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == b.get()); 243 } 244 245 TEST(AtomicOpsTest, Increment) { 246 // Create and start lots of threads. 247 AtomicOpRunner<IncrementOp, UniqueValueVerifier> runner(0); 248 ScopedPtrCollection<Thread> threads; 249 StartThreads(&threads, &runner); 250 runner.SetExpectedThreadCount(kNumThreads); 251 252 // Release the hounds! 253 EXPECT_TRUE(runner.Run()); 254 EXPECT_EQ(kOperationsToRun * kNumThreads, runner.shared_value()); 255 } 256 257 TEST(AtomicOpsTest, Decrement) { 258 // Create and start lots of threads. 259 AtomicOpRunner<DecrementOp, UniqueValueVerifier> runner( 260 kOperationsToRun * kNumThreads); 261 ScopedPtrCollection<Thread> threads; 262 StartThreads(&threads, &runner); 263 runner.SetExpectedThreadCount(kNumThreads); 264 265 // Release the hounds! 266 EXPECT_TRUE(runner.Run()); 267 EXPECT_EQ(0, runner.shared_value()); 268 } 269 270 TEST(AtomicOpsTest, CompareAndSwap) { 271 // Create and start lots of threads. 272 AtomicOpRunner<CompareAndSwapOp, CompareAndSwapVerifier> runner(0); 273 ScopedPtrCollection<Thread> threads; 274 StartThreads(&threads, &runner); 275 runner.SetExpectedThreadCount(kNumThreads); 276 277 // Release the hounds! 278 EXPECT_TRUE(runner.Run()); 279 EXPECT_EQ(1, runner.shared_value()); 280 } 281 282 TEST(GlobalLockTest, Basic) { 283 // Create and start lots of threads. 284 LockRunner<GlobalLock> runner; 285 ScopedPtrCollection<Thread> threads; 286 StartThreads(&threads, &runner); 287 runner.SetExpectedThreadCount(kNumThreads); 288 289 // Release the hounds! 290 EXPECT_TRUE(runner.Run()); 291 EXPECT_EQ(0, runner.shared_value()); 292 } 293 294 TEST(CriticalSectionTest, Basic) { 295 // Create and start lots of threads. 296 LockRunner<CriticalSectionLock> runner; 297 ScopedPtrCollection<Thread> threads; 298 StartThreads(&threads, &runner); 299 runner.SetExpectedThreadCount(kNumThreads); 300 301 // Release the hounds! 302 EXPECT_TRUE(runner.Run()); 303 EXPECT_EQ(0, runner.shared_value()); 304 } 305 306 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) 307 TEST(CriticalSectionTest, IsLocked) { 308 // Simple single-threaded test of IsLocked. 309 CriticalSection cs; 310 EXPECT_FALSE(cs.IsLocked()); 311 cs.Enter(); 312 EXPECT_TRUE(cs.IsLocked()); 313 cs.Leave(); 314 EXPECT_FALSE(cs.IsLocked()); 315 if (!cs.TryEnter()) 316 FAIL(); 317 EXPECT_TRUE(cs.IsLocked()); 318 cs.Leave(); 319 EXPECT_FALSE(cs.IsLocked()); 320 } 321 #endif 322 323 } // namespace rtc 324