1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 #include "mozilla/CondVar.h"
8 #include "mozilla/Monitor.h"
9 #include "mozilla/ReentrantMonitor.h"
10 #include "mozilla/Mutex.h"
11 #include "gtest/gtest.h"
12 
13 using namespace mozilla;
14 
15 static PRThread*
spawn(void (* run)(void *),void * arg)16 spawn(void (*run)(void*), void* arg)
17 {
18   return PR_CreateThread(PR_SYSTEM_THREAD,
19                          run,
20                          arg,
21                          PR_PRIORITY_NORMAL,
22                          PR_GLOBAL_THREAD,
23                          PR_JOINABLE_THREAD,
24                          0);
25 }
26 
27 //-----------------------------------------------------------------------------
28 // Sanity check: tests that can be done on a single thread
29 //
TEST(Synchronization,Sanity)30 TEST(Synchronization, Sanity)
31 {
32   Mutex lock("sanity::lock");
33   lock.Lock();
34   lock.AssertCurrentThreadOwns();
35   lock.Unlock();
36 
37   {
38     MutexAutoLock autolock(lock);
39     lock.AssertCurrentThreadOwns();
40   }
41 
42   lock.Lock();
43   lock.AssertCurrentThreadOwns();
44   {
45     MutexAutoUnlock autounlock(lock);
46   }
47   lock.AssertCurrentThreadOwns();
48   lock.Unlock();
49 
50   ReentrantMonitor mon("sanity::monitor");
51   mon.Enter();
52   mon.AssertCurrentThreadIn();
53   mon.Enter();
54   mon.AssertCurrentThreadIn();
55   mon.Exit();
56   mon.AssertCurrentThreadIn();
57   mon.Exit();
58 
59   {
60     ReentrantMonitorAutoEnter automon(mon);
61     mon.AssertCurrentThreadIn();
62   }
63 }
64 
65 //-----------------------------------------------------------------------------
66 // Mutex contention tests
67 //
68 static Mutex* gLock1;
69 
70 static void
MutexContention_thread(void *)71 MutexContention_thread(void* /*arg*/)
72 {
73   for (int i = 0; i < 100000; ++i) {
74     gLock1->Lock();
75     gLock1->AssertCurrentThreadOwns();
76     gLock1->Unlock();
77   }
78 }
79 
TEST(Synchronization,MutexContention)80 TEST(Synchronization, MutexContention)
81 {
82   gLock1 = new Mutex("lock1");
83   // PURPOSELY not checking for OOM.  YAY!
84 
85   PRThread* t1 = spawn(MutexContention_thread, nullptr);
86   PRThread* t2 = spawn(MutexContention_thread, nullptr);
87   PRThread* t3 = spawn(MutexContention_thread, nullptr);
88 
89   PR_JoinThread(t1);
90   PR_JoinThread(t2);
91   PR_JoinThread(t3);
92 
93   delete gLock1;
94 }
95 
96 //-----------------------------------------------------------------------------
97 // Monitor tests
98 //
99 static Monitor* gMon1;
100 
101 static void
MonitorContention_thread(void *)102 MonitorContention_thread(void* /*arg*/)
103 {
104   for (int i = 0; i < 100000; ++i) {
105     gMon1->Lock();
106     gMon1->AssertCurrentThreadOwns();
107     gMon1->Unlock();
108   }
109 }
110 
TEST(Synchronization,MonitorContention)111 TEST(Synchronization, MonitorContention)
112 {
113   gMon1 = new Monitor("mon1");
114 
115   PRThread* t1 = spawn(MonitorContention_thread, nullptr);
116   PRThread* t2 = spawn(MonitorContention_thread, nullptr);
117   PRThread* t3 = spawn(MonitorContention_thread, nullptr);
118 
119   PR_JoinThread(t1);
120   PR_JoinThread(t2);
121   PR_JoinThread(t3);
122 
123   delete gMon1;
124 }
125 
126 
127 static ReentrantMonitor* gMon2;
128 
129 static void
MonitorContention2_thread(void *)130 MonitorContention2_thread(void* /*arg*/)
131 {
132   for (int i = 0; i < 100000; ++i) {
133     gMon2->Enter();
134     gMon2->AssertCurrentThreadIn();
135     {
136       gMon2->Enter();
137       gMon2->AssertCurrentThreadIn();
138       gMon2->Exit();
139     }
140     gMon2->AssertCurrentThreadIn();
141     gMon2->Exit();
142   }
143 }
144 
TEST(Synchronization,MonitorContention2)145 TEST(Synchronization, MonitorContention2)
146 {
147   gMon2 = new ReentrantMonitor("mon1");
148 
149   PRThread* t1 = spawn(MonitorContention2_thread, nullptr);
150   PRThread* t2 = spawn(MonitorContention2_thread, nullptr);
151   PRThread* t3 = spawn(MonitorContention2_thread, nullptr);
152 
153   PR_JoinThread(t1);
154   PR_JoinThread(t2);
155   PR_JoinThread(t3);
156 
157   delete gMon2;
158 }
159 
160 
161 static ReentrantMonitor* gMon3;
162 static int32_t gMonFirst;
163 
164 static void
MonitorSyncSanity_thread(void *)165 MonitorSyncSanity_thread(void* /*arg*/)
166 {
167   gMon3->Enter();
168   gMon3->AssertCurrentThreadIn();
169   if (gMonFirst) {
170     gMonFirst = 0;
171     gMon3->Wait();
172     gMon3->Enter();
173   } else {
174     gMon3->Notify();
175     gMon3->Enter();
176   }
177   gMon3->AssertCurrentThreadIn();
178   gMon3->Exit();
179   gMon3->AssertCurrentThreadIn();
180   gMon3->Exit();
181 }
182 
TEST(Synchronization,MonitorSyncSanity)183 TEST(Synchronization, MonitorSyncSanity)
184 {
185   gMon3 = new ReentrantMonitor("monitor::syncsanity");
186 
187   for (int32_t i = 0; i < 10000; ++i) {
188     gMonFirst = 1;
189     PRThread* ping = spawn(MonitorSyncSanity_thread, nullptr);
190     PRThread* pong = spawn(MonitorSyncSanity_thread, nullptr);
191     PR_JoinThread(ping);
192     PR_JoinThread(pong);
193   }
194 
195   delete gMon3;
196 }
197 
198 //-----------------------------------------------------------------------------
199 // Condvar tests
200 //
201 static Mutex* gCvlock1;
202 static CondVar* gCv1;
203 static int32_t gCvFirst;
204 
205 static void
CondVarSanity_thread(void *)206 CondVarSanity_thread(void* /*arg*/)
207 {
208   gCvlock1->Lock();
209   gCvlock1->AssertCurrentThreadOwns();
210   if (gCvFirst) {
211     gCvFirst = 0;
212     gCv1->Wait();
213   } else {
214     gCv1->Notify();
215   }
216   gCvlock1->AssertCurrentThreadOwns();
217   gCvlock1->Unlock();
218 }
219 
TEST(Synchronization,CondVarSanity)220 TEST(Synchronization, CondVarSanity)
221 {
222   gCvlock1 = new Mutex("cvlock1");
223   gCv1 = new CondVar(*gCvlock1, "cvlock1");
224 
225   for (int32_t i = 0; i < 10000; ++i) {
226     gCvFirst = 1;
227     PRThread* ping = spawn(CondVarSanity_thread, nullptr);
228     PRThread* pong = spawn(CondVarSanity_thread, nullptr);
229     PR_JoinThread(ping);
230     PR_JoinThread(pong);
231   }
232 
233   delete gCv1;
234   delete gCvlock1;
235 }
236 
237 //-----------------------------------------------------------------------------
238 // AutoLock tests
239 //
TEST(Synchronization,AutoLock)240 TEST(Synchronization, AutoLock)
241 {
242   Mutex l1("autolock");
243   MutexAutoLock autol1(l1);
244 
245   l1.AssertCurrentThreadOwns();
246 
247   {
248     Mutex l2("autolock2");
249     MutexAutoLock autol2(l2);
250 
251     l1.AssertCurrentThreadOwns();
252     l2.AssertCurrentThreadOwns();
253   }
254 
255   l1.AssertCurrentThreadOwns();
256 }
257 
258 //-----------------------------------------------------------------------------
259 // AutoUnlock tests
260 //
TEST(Synchronization,AutoUnlock)261 TEST(Synchronization, AutoUnlock)
262 {
263   Mutex l1("autounlock");
264   Mutex l2("autounlock2");
265 
266   l1.Lock();
267   l1.AssertCurrentThreadOwns();
268 
269   {
270     MutexAutoUnlock autol1(l1);
271     {
272       l2.Lock();
273       l2.AssertCurrentThreadOwns();
274 
275       MutexAutoUnlock autol2(l2);
276     }
277     l2.AssertCurrentThreadOwns();
278     l2.Unlock();
279   }
280   l1.AssertCurrentThreadOwns();
281 
282   l1.Unlock();
283 }
284 
285 //-----------------------------------------------------------------------------
286 // AutoMonitor tests
287 //
TEST(Synchronization,AutoMonitor)288 TEST(Synchronization, AutoMonitor)
289 {
290   ReentrantMonitor m1("automonitor");
291   ReentrantMonitor m2("automonitor2");
292 
293   m1.Enter();
294   m1.AssertCurrentThreadIn();
295   {
296     ReentrantMonitorAutoEnter autom1(m1);
297     m1.AssertCurrentThreadIn();
298 
299     m2.Enter();
300     m2.AssertCurrentThreadIn();
301     {
302       ReentrantMonitorAutoEnter autom2(m2);
303       m1.AssertCurrentThreadIn();
304       m2.AssertCurrentThreadIn();
305     }
306     m2.AssertCurrentThreadIn();
307     m2.Exit();
308 
309     m1.AssertCurrentThreadIn();
310   }
311   m1.AssertCurrentThreadIn();
312   m1.Exit();
313 }
314