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:
Verify(const std::vector<int> & values)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 
Finalize()42   void Finalize() {}
43 
44  private:
45   std::set<int> all_values_;
46 };
47 
48 class CompareAndSwapVerifier {
49  public:
CompareAndSwapVerifier()50   CompareAndSwapVerifier() : zero_count_(0) {}
51 
Verify(const std::vector<int> & values)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 
Finalize()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:
RunnerBase(int value)72   explicit RunnerBase(int value)
73       : threads_active_(0),
74         start_event_(true, false),
75         done_event_(true, false),
76         shared_value_(value) {}
77 
Run()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 
SetExpectedThreadCount(int count)86   void SetExpectedThreadCount(int count) {
87     threads_active_ = count;
88   }
89 
shared_value() const90   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.
BeforeStart()95   void BeforeStart() {
96     ASSERT_TRUE(start_event_.Wait(kLongTime));
97   }
98 
99   // Returns true if all threads have finished.
AfterEnd()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:
Lock()116   void Lock() EXCLUSIVE_LOCK_FUNCTION() {
117     cs_.Enter();
118   }
Unlock()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:
LockRunner()130   LockRunner() : RunnerBase(0) {}
131 
OnMessage(Message * msg)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:
AtomicOpRunner(int initial_value)159   explicit AtomicOpRunner(int initial_value) : RunnerBase(initial_value) {}
160 
OnMessage(Message * msg)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 {
AtomicOprtc::__anon84f8cdda0111::IncrementOp188   static int AtomicOp(int* i) { return AtomicOps::Increment(i); }
189 };
190 
191 struct DecrementOp {
AtomicOprtc::__anon84f8cdda0111::DecrementOp192   static int AtomicOp(int* i) { return AtomicOps::Decrement(i); }
193 };
194 
195 struct CompareAndSwapOp {
AtomicOprtc::__anon84f8cdda0111::CompareAndSwapOp196   static int AtomicOp(int* i) { return AtomicOps::CompareAndSwap(i, 0, 1); }
197 };
198 
StartThreads(ScopedPtrCollection<Thread> * threads,MessageHandler * handler)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 
TEST(AtomicOpsTest,Simple)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 
TEST(AtomicOpsTest,SimplePtr)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 
TEST(AtomicOpsTest,Increment)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 
TEST(AtomicOpsTest,Decrement)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 
TEST(AtomicOpsTest,CompareAndSwap)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 
TEST(GlobalLockTest,Basic)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 
TEST(CriticalSectionTest,Basic)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)
TEST(CriticalSectionTest,IsLocked)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