1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: sw=2 ts=4 et :
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 // Avoid DMD-specific parts of MOZ_DEFINE_MALLOC_SIZE_OF
8 #undef MOZ_DMD
9 
10 #include "nsIMemoryReporter.h"
11 #include "mozilla/Mutex.h"
12 
13 #include "gtest/gtest.h"
14 
15 //-----------------------------------------------------------------------------
16 
AllocLockRecurseUnlockFree(int i)17 static void AllocLockRecurseUnlockFree(int i) {
18   if (0 == i) return;
19 
20   mozilla::Mutex* lock = new mozilla::Mutex("deadlockDetector.scalability.t1");
21   {
22     mozilla::MutexAutoLock _(*lock);
23     AllocLockRecurseUnlockFree(i - 1);
24   }
25   delete lock;
26 }
27 
28 // This test creates a resource dependency chain N elements long, then
29 // frees all the resources in the chain.
TEST(DeadlockDetectorScalability,LengthNDepChain)30 TEST(DeadlockDetectorScalability, LengthNDepChain)
31 {
32   const int N = 1 << 14;  // 16K
33   AllocLockRecurseUnlockFree(N);
34   ASSERT_TRUE(true);
35 }
36 
37 //-----------------------------------------------------------------------------
38 
39 // This test creates a single lock that is ordered < N resources, then
40 // repeatedly exercises this order k times.
41 //
42 // NB: It takes a minute or two to run so it is disabled by default.
TEST(DeadlockDetectorScalability,DISABLED_OneLockNDeps)43 TEST(DeadlockDetectorScalability, DISABLED_OneLockNDeps)
44 {
45   // NB: Using a larger test size to stress our traversal logic.
46   const int N = 1 << 17;  // 131k
47   const int K = 100;
48 
49   mozilla::Mutex* lock =
50       new mozilla::Mutex("deadlockDetector.scalability.t2.master");
51   mozilla::Mutex** locks = new mozilla::Mutex*[N];
52   if (!locks) MOZ_CRASH("couldn't allocate lock array");
53 
54   for (int i = 0; i < N; ++i)
55     locks[i] = new mozilla::Mutex("deadlockDetector.scalability.t2.dep");
56 
57   // establish orders
58   {
59     mozilla::MutexAutoLock m(*lock);
60     for (int i = 0; i < N; ++i) mozilla::MutexAutoLock s(*locks[i]);
61   }
62 
63   // exercise order check
64   {
65     mozilla::MutexAutoLock m(*lock);
66     for (int i = 0; i < K; ++i)
67       for (int j = 0; j < N; ++j) mozilla::MutexAutoLock s(*locks[i]);
68   }
69 
70   for (int i = 0; i < N; ++i) delete locks[i];
71   delete[] locks;
72 
73   ASSERT_TRUE(true);
74 }
75 
76 //-----------------------------------------------------------------------------
77 
78 // This test creates N resources and adds the theoretical maximum number
79 // of dependencies, O(N^2).  It then repeats that sequence of
80 // acquisitions k times.  Finally, all resources are freed.
81 //
82 // It's very difficult to perform well on this test.  It's put forth as a
83 // challenge problem.
84 
TEST(DeadlockDetectorScalability,MaxDepsNsq)85 TEST(DeadlockDetectorScalability, MaxDepsNsq)
86 {
87   const int N = 1 << 10;  // 1k
88   const int K = 10;
89 
90   mozilla::Mutex** locks = new mozilla::Mutex*[N];
91   if (!locks) MOZ_CRASH("couldn't allocate lock array");
92 
93   for (int i = 0; i < N; ++i)
94     locks[i] = new mozilla::Mutex("deadlockDetector.scalability.t3");
95 
96   for (int i = 0; i < N; ++i) {
97     mozilla::MutexAutoLock al1(*locks[i]);
98     for (int j = i + 1; j < N; ++j) mozilla::MutexAutoLock al2(*locks[j]);
99   }
100 
101   for (int i = 0; i < K; ++i) {
102     for (int j = 0; j < N; ++j) {
103       mozilla::MutexAutoLock al1(*locks[j]);
104       for (int k = j + 1; k < N; ++k) mozilla::MutexAutoLock al2(*locks[k]);
105     }
106   }
107 
108   for (int i = 0; i < N; ++i) delete locks[i];
109   delete[] locks;
110 
111   ASSERT_TRUE(true);
112 }
113 
114 //-----------------------------------------------------------------------------
115 
116 // This test creates a single lock that is ordered < N resources. The
117 // resources are allocated, exercised K times, and deallocated one at
118 // a time.
119 
TEST(DeadlockDetectorScalability,OneLockNDepsUsedSeveralTimes)120 TEST(DeadlockDetectorScalability, OneLockNDepsUsedSeveralTimes)
121 {
122   const size_t N = 1 << 17;  // 131k
123   const size_t K = 3;
124 
125   // Create master lock.
126   mozilla::Mutex* lock_1 =
127       new mozilla::Mutex("deadlockDetector.scalability.t4.master");
128   for (size_t n = 0; n < N; n++) {
129     // Create child lock.
130     mozilla::Mutex* lock_2 =
131         new mozilla::Mutex("deadlockDetector.scalability.t4.child");
132 
133     // First lock the master.
134     mozilla::MutexAutoLock m(*lock_1);
135 
136     // Now lock and unlock the child a few times.
137     for (size_t k = 0; k < K; k++) {
138       mozilla::MutexAutoLock c(*lock_2);
139     }
140 
141     // Destroy the child lock.
142     delete lock_2;
143   }
144 
145   // Cleanup the master lock.
146   delete lock_1;
147 
148   ASSERT_TRUE(true);
149 }
150 
151 //-----------------------------------------------------------------------------
152 
153 MOZ_DEFINE_MALLOC_SIZE_OF(DeadlockDetectorMallocSizeOf)
154 
155 // This is a simple test that exercises the deadlock detector memory reporting
156 // functionality.
TEST(DeadlockDetectorScalability,SizeOf)157 TEST(DeadlockDetectorScalability, SizeOf)
158 {
159   size_t memory_used = mozilla::BlockingResourceBase::SizeOfDeadlockDetector(
160       DeadlockDetectorMallocSizeOf);
161 
162   ASSERT_GT(memory_used, size_t(0));
163 }
164