1 : // Copyright 2014 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/common/recursive_lock.h"
16 :
17 : #include "base/memory/scoped_vector.h"
18 : #include "base/threading/simple_thread.h"
19 : #include "gtest/gtest.h"
20 :
21 : namespace common {
22 :
23 : namespace {
24 :
25 : // We use a thread-safe random function to avoid all of the various threads
26 : // consistently producing the exact same random values.
27 E : size_t Rand(size_t min, size_t max) {
28 E : static base::Lock lock;
29 E : base::AutoLock auto_lock(lock);
30 E : size_t r = rand();
31 E : double v = static_cast<double>(r) / static_cast<double>(RAND_MAX);
32 E : v *= max - min;
33 E : r = min + static_cast<size_t>(::round(v));
34 E : return r;
35 E : }
36 :
37 : // A thread that grabs a recursive lock repeatedly, to random recursion depths.
38 : class RecursiveLockTestRunner : public base::DelegateSimpleThread::Delegate {
39 : public:
40 E : RecursiveLockTestRunner(size_t lock_count, RecursiveLock* recursive_lock)
41 : : lock_count_(lock_count), recursive_lock_(recursive_lock) {
42 E : }
43 :
44 E : virtual void Run() {
45 : // The precision of 'Sleep' is in ticks, and we want to wait for 0 or 1
46 : // ticks.
47 : static const size_t kOneTickInMs = 15;
48 : static const size_t kMaxTryCount = 40;
49 :
50 : // Repeatedly grab the lock, with varying recursion depths.
51 E : while (lock_count_ > 0) {
52 E : ::Sleep(kOneTickInMs * Rand(0, 1));
53 :
54 : // Choose a random depth with which to acquire this thread.
55 E : size_t depth = Rand(1, 40);
56 E : for (size_t i = 0; i < depth; ++i) {
57 : // Every second time we acquire the lock we try it with 'try'.
58 E : if ((i % 2) == 0) {
59 : // Try to acquire a few times.
60 E : size_t try_count = 0;
61 E : while (try_count < kMaxTryCount && !recursive_lock_->Try()) {
62 E : ++try_count;
63 E : ::Sleep(kOneTickInMs * Rand(0, 1));
64 E : }
65 :
66 : // If we didn't acquire the lock by calling 'Try', then grab it
67 : // with a blocking acquisition.
68 E : if (try_count == kMaxTryCount)
69 E : recursive_lock_->Acquire();
70 E : } else {
71 : // Otherwise, simply block on the lock.
72 E : recursive_lock_->Acquire();
73 : }
74 E : }
75 :
76 : // Release the thread half of the number of times.
77 E : for (size_t i = 0; i < depth / 2; ++i)
78 E : recursive_lock_->Release();
79 :
80 : // Grab and release it a secondary time. This causes an 'up down up down'
81 : // motion on the recursion depth.
82 E : size_t depth1 = Rand(0, 20);
83 E : for (size_t i = 0; i < depth1; ++i)
84 E : recursive_lock_->Acquire();
85 E : for (size_t i = 0; i < depth1; ++i)
86 E : recursive_lock_->Release();
87 :
88 : // And release the rest of the initial acquisitions.
89 E : for (size_t i = depth / 2; i < depth; ++i)
90 E : recursive_lock_->Release();
91 :
92 E : --lock_count_;
93 E : }
94 E : }
95 :
96 : private:
97 : // The number of times this thread should grab the lock.
98 : size_t lock_count_;
99 : // The lock that is being grabbed.
100 : RecursiveLock* recursive_lock_;
101 : };
102 :
103 : } // namespace
104 :
105 E : TEST(RecursiveLock, StressTest) {
106 : static const size_t kCyclesPerThread = 100;
107 : static const size_t kThreadCount = 50;
108 E : RecursiveLock lock;
109 :
110 E : lock.Acquire();
111 E : ScopedVector<RecursiveLockTestRunner> runners;
112 E : ScopedVector<base::DelegateSimpleThread> threads;
113 E : for (size_t i = 0; i < kThreadCount; ++i) {
114 E : runners.push_back(new RecursiveLockTestRunner(kCyclesPerThread, &lock));
115 : threads.push_back(new base::DelegateSimpleThread(runners.back(),
116 E : "RecursiveLockTest"));
117 E : threads.back()->Start();
118 E : }
119 E : lock.Release();
120 :
121 E : for (size_t i = 0; i < threads.size(); ++i)
122 E : threads[i]->Join();
123 E : }
124 :
125 : } // namespace common
|