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