1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7 ** File: lock.c
8 ** Purpose: test basic locking functions
9 **
10 ** Modification History:
11 ** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
12 ** The debug mode will print all of the printfs associated with this test.
13 ** The regress mode will be the default mode. Since the regress tool limits
14 ** the output to a one line status:PASS or FAIL,all of the printf statements
15 ** have been handled with an if (debug_mode) statement.
16 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
17 ** recognize the return code from tha main program.
18 **
19 ** 11-Aug-97 LarryH. Win16 port of NSPR.
20 ** - Added "PASS", "FAIL" messages on completion.
21 ** - Change stack variables to static scope variables
22 ** because of shadow-stack use by Win16
23 ** - Added PR_CALLBACK attribute to functions called by NSPR
24 ** - Added command line arguments:
25 ** - l <num> to control the number of loops
26 ** - c <num> to control the number of CPUs.
27 ** (was positional argv).
28 **
29 **
30 ***********************************************************************/
31
32 /***********************************************************************
33 ** Includes
34 ***********************************************************************/
35 /* Used to get the command line option */
36 #include "plgetopt.h"
37
38 #include "prio.h"
39 #include "prcmon.h"
40 #include "prinit.h"
41 #include "prinrval.h"
42 #include "prprf.h"
43 #include "prlock.h"
44 #include "prlog.h"
45 #include "prmon.h"
46 #include "prmem.h"
47 #include "prthread.h"
48 #include "prtypes.h"
49
50 #include "plstr.h"
51
52 #include <stdlib.h>
53
54 #if defined(XP_UNIX)
55 #include <string.h>
56 #endif
57
58 static PRIntn failed_already=0;
59 static PRFileDesc *std_err = NULL;
60 static PRBool verbosity = PR_FALSE;
61 static PRBool debug_mode = PR_FALSE;
62
63 const static PRIntervalTime contention_interval = 50;
64
65 typedef struct LockContentious_s {
66 PRLock *ml;
67 PRInt32 loops;
68 PRUint32 contender;
69 PRUint32 contentious;
70 PRIntervalTime overhead;
71 PRIntervalTime interval;
72 } LockContentious_t;
73
74 typedef struct MonitorContentious_s {
75 PRMonitor *ml;
76 PRInt32 loops;
77 PRUint32 contender;
78 PRUint32 contentious;
79 PRIntervalTime overhead;
80 PRIntervalTime interval;
81 } MonitorContentious_t;
82
83
Sleeper(PRUint32 loops)84 static PRIntervalTime Sleeper(PRUint32 loops)
85 {
86 PRIntervalTime predicted = 0;
87 while (loops-- > 0)
88 {
89 predicted += contention_interval;
90 (void)PR_Sleep(contention_interval);
91 }
92 return predicted;
93 } /* Sleeper */
94
95 /*
96 ** BASIC LOCKS
97 */
MakeLock(PRUint32 loops)98 static PRIntervalTime MakeLock(PRUint32 loops)
99 {
100 PRLock *ml = NULL;
101 while (loops-- > 0)
102 {
103 ml = PR_NewLock();
104 PR_DestroyLock(ml);
105 ml = NULL;
106 }
107 return 0;
108 } /* MakeLock */
109
NonContentiousLock(PRUint32 loops)110 static PRIntervalTime NonContentiousLock(PRUint32 loops)
111 {
112 PRLock *ml = NULL;
113 ml = PR_NewLock();
114 while (loops-- > 0)
115 {
116 PR_Lock(ml);
117 PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(ml);
118 PR_Unlock(ml);
119 }
120 PR_DestroyLock(ml);
121 return 0;
122 } /* NonContentiousLock */
123
LockContender(void * arg)124 static void PR_CALLBACK LockContender(void *arg)
125 {
126 LockContentious_t *contention = (LockContentious_t*)arg;
127 while (contention->loops-- > 0)
128 {
129 PR_Lock(contention->ml);
130 PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
131 contention->contender+= 1;
132 contention->overhead += contention->interval;
133 PR_Sleep(contention->interval);
134 PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
135 PR_Unlock(contention->ml);
136 }
137 } /* LockContender */
138
ContentiousLock(PRUint32 loops)139 static PRIntervalTime ContentiousLock(PRUint32 loops)
140 {
141 PRStatus status;
142 PRThread *thread = NULL;
143 LockContentious_t * contention;
144 PRIntervalTime rv, overhead, timein = PR_IntervalNow();
145
146 contention = PR_NEWZAP(LockContentious_t);
147 contention->loops = loops;
148 contention->overhead = 0;
149 contention->ml = PR_NewLock();
150 contention->interval = contention_interval;
151 thread = PR_CreateThread(
152 PR_USER_THREAD, LockContender, contention,
153 PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
154 PR_ASSERT(thread != NULL);
155
156 overhead = PR_IntervalNow() - timein;
157
158 while (contention->loops-- > 0)
159 {
160 PR_Lock(contention->ml);
161 PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
162 contention->contentious+= 1;
163 contention->overhead += contention->interval;
164 PR_Sleep(contention->interval);
165 PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
166 PR_Unlock(contention->ml);
167 }
168
169 timein = PR_IntervalNow();
170 status = PR_JoinThread(thread);
171 PR_DestroyLock(contention->ml);
172 overhead += (PR_IntervalNow() - timein);
173 rv = overhead + contention->overhead;
174 if (verbosity)
175 PR_fprintf(
176 std_err, "Access ratio: %u to %u\n",
177 contention->contentious, contention->contender);
178 PR_Free(contention);
179 return rv;
180 } /* ContentiousLock */
181
182 /*
183 ** MONITORS
184 */
MakeMonitor(PRUint32 loops)185 static PRIntervalTime MakeMonitor(PRUint32 loops)
186 {
187 PRMonitor *ml = NULL;
188 while (loops-- > 0)
189 {
190 ml = PR_NewMonitor();
191 PR_DestroyMonitor(ml);
192 ml = NULL;
193 }
194 return 0;
195 } /* MakeMonitor */
196
NonContentiousMonitor(PRUint32 loops)197 static PRIntervalTime NonContentiousMonitor(PRUint32 loops)
198 {
199 PRMonitor *ml = NULL;
200 ml = PR_NewMonitor();
201 while (loops-- > 0)
202 {
203 PR_EnterMonitor(ml);
204 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
205 PR_ExitMonitor(ml);
206 }
207 PR_DestroyMonitor(ml);
208 return 0;
209 } /* NonContentiousMonitor */
210
TryEntry(void * arg)211 static void PR_CALLBACK TryEntry(void *arg)
212 {
213 PRMonitor *ml = (PRMonitor*)arg;
214 if (debug_mode) PR_fprintf(std_err, "Reentrant thread created\n");
215 PR_EnterMonitor(ml);
216 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
217 if (debug_mode) PR_fprintf(std_err, "Reentrant thread acquired monitor\n");
218 PR_ExitMonitor(ml);
219 if (debug_mode) PR_fprintf(std_err, "Reentrant thread released monitor\n");
220 } /* TryEntry */
221
ReentrantMonitor(PRUint32 loops)222 static PRIntervalTime ReentrantMonitor(PRUint32 loops)
223 {
224 PRStatus status;
225 PRThread *thread;
226 PRMonitor *ml = PR_NewMonitor();
227 if (debug_mode) PR_fprintf(std_err, "\nMonitor created for reentrant test\n");
228
229 PR_EnterMonitor(ml);
230 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
231 PR_EnterMonitor(ml);
232 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
233 if (debug_mode) PR_fprintf(std_err, "Monitor acquired twice\n");
234
235 thread = PR_CreateThread(
236 PR_USER_THREAD, TryEntry, ml,
237 PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
238 PR_ASSERT(thread != NULL);
239 PR_Sleep(PR_SecondsToInterval(1));
240 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
241
242 PR_ExitMonitor(ml);
243 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
244 if (debug_mode) PR_fprintf(std_err, "Monitor released first time\n");
245
246 PR_ExitMonitor(ml);
247 if (debug_mode) PR_fprintf(std_err, "Monitor released second time\n");
248
249 status = PR_JoinThread(thread);
250 if (debug_mode) PR_fprintf(std_err,
251 "Reentrant thread joined %s\n",
252 (status == PR_SUCCESS) ? "successfully" : "in error");
253
254 PR_DestroyMonitor(ml);
255 return 0;
256 } /* ReentrantMonitor */
257
MonitorContender(void * arg)258 static void PR_CALLBACK MonitorContender(void *arg)
259 {
260 MonitorContentious_t *contention = (MonitorContentious_t*)arg;
261 while (contention->loops-- > 0)
262 {
263 PR_EnterMonitor(contention->ml);
264 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
265 contention->contender+= 1;
266 contention->overhead += contention->interval;
267 PR_Sleep(contention->interval);
268 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
269 PR_ExitMonitor(contention->ml);
270 }
271 } /* MonitorContender */
272
ContentiousMonitor(PRUint32 loops)273 static PRUint32 ContentiousMonitor(PRUint32 loops)
274 {
275 PRStatus status;
276 PRThread *thread = NULL;
277 MonitorContentious_t * contention;
278 PRIntervalTime rv, overhead, timein = PR_IntervalNow();
279
280 contention = PR_NEWZAP(MonitorContentious_t);
281 contention->loops = loops;
282 contention->overhead = 0;
283 contention->ml = PR_NewMonitor();
284 contention->interval = contention_interval;
285 thread = PR_CreateThread(
286 PR_USER_THREAD, MonitorContender, contention,
287 PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
288 PR_ASSERT(thread != NULL);
289
290 overhead = PR_IntervalNow() - timein;
291
292 while (contention->loops-- > 0)
293 {
294 PR_EnterMonitor(contention->ml);
295 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
296 contention->contentious+= 1;
297 contention->overhead += contention->interval;
298 PR_Sleep(contention->interval);
299 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
300 PR_ExitMonitor(contention->ml);
301 }
302
303 timein = PR_IntervalNow();
304 status = PR_JoinThread(thread);
305 PR_DestroyMonitor(contention->ml);
306 overhead += (PR_IntervalNow() - timein);
307 rv = overhead + contention->overhead;
308 if (verbosity)
309 PR_fprintf(
310 std_err, "Access ratio: %u to %u\n",
311 contention->contentious, contention->contender);
312 PR_Free(contention);
313 return rv;
314 } /* ContentiousMonitor */
315
316 /*
317 ** CACHED MONITORS
318 */
NonContentiousCMonitor(PRUint32 loops)319 static PRIntervalTime NonContentiousCMonitor(PRUint32 loops)
320 {
321 MonitorContentious_t contention;
322 while (loops-- > 0)
323 {
324 PR_CEnterMonitor(&contention);
325 PR_CExitMonitor(&contention);
326 }
327 return 0;
328 } /* NonContentiousCMonitor */
329
Contender(void * arg)330 static void PR_CALLBACK Contender(void *arg)
331 {
332 MonitorContentious_t *contention = (MonitorContentious_t*)arg;
333 while (contention->loops-- > 0)
334 {
335 PR_CEnterMonitor(contention);
336 contention->contender+= 1;
337 contention->overhead += contention->interval;
338 PR_Sleep(contention->interval);
339 PR_CExitMonitor(contention);
340 }
341 } /* Contender */
342
ContentiousCMonitor(PRUint32 loops)343 static PRIntervalTime ContentiousCMonitor(PRUint32 loops)
344 {
345 PRStatus status;
346 PRThread *thread = NULL;
347 MonitorContentious_t * contention;
348 PRIntervalTime overhead, timein = PR_IntervalNow();
349
350 contention = PR_NEWZAP(MonitorContentious_t);
351 contention->ml = NULL;
352 contention->loops = loops;
353 contention->interval = contention_interval;
354 thread = PR_CreateThread(
355 PR_USER_THREAD, Contender, contention,
356 PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
357 PR_ASSERT(thread != NULL);
358
359 overhead = PR_IntervalNow() - timein;
360
361 while (contention->loops-- > 0)
362 {
363 PR_CEnterMonitor(contention);
364 contention->contentious+= 1;
365 contention->overhead += contention->interval;
366 PR_Sleep(contention->interval);
367 PR_CExitMonitor(contention);
368 }
369
370 timein = PR_IntervalNow();
371 status = PR_JoinThread(thread);
372 overhead += (PR_IntervalNow() - timein);
373 overhead += overhead + contention->overhead;
374 if (verbosity)
375 PR_fprintf(
376 std_err, "Access ratio: %u to %u\n",
377 contention->contentious, contention->contender);
378 PR_Free(contention);
379 return overhead;
380 } /* ContentiousCMonitor */
381
Test(const char * msg,PRUint32 (* test)(PRUint32 loops),PRUint32 loops,PRIntervalTime overhead)382 static PRIntervalTime Test(
383 const char* msg, PRUint32 (*test)(PRUint32 loops),
384 PRUint32 loops, PRIntervalTime overhead)
385 {
386 /*
387 * overhead - overhead not measured by the test.
388 * duration - wall clock time it took to perform test.
389 * predicted - extra time test says should not be counted
390 *
391 * Time accountable to the test is duration - overhead - predicted
392 * All times are Intervals and accumulated for all iterations.
393 */
394 PRFloat64 elapsed;
395 PRIntervalTime accountable, duration;
396 PRUintn spaces = PL_strlen(msg);
397 PRIntervalTime timeout, timein = PR_IntervalNow();
398 PRIntervalTime predicted = test(loops);
399 timeout = PR_IntervalNow();
400 duration = timeout - timein;
401
402 if (debug_mode)
403 {
404 accountable = duration - predicted;
405 accountable -= overhead;
406 elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable);
407 PR_fprintf(PR_STDOUT, "%s:", msg);
408 while (spaces++ < 50) PR_fprintf(PR_STDOUT, " ");
409 if ((PRInt32)accountable < 0)
410 PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n");
411 else
412 PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed/loops);
413 }
414 return duration;
415 } /* Test */
416
main(int argc,char ** argv)417 int main(int argc, char **argv)
418 {
419 PRBool rv = PR_TRUE;
420 PRIntervalTime duration;
421 PRUint32 cpu, cpus = 2, loops = 100;
422
423
424 PR_STDIO_INIT();
425 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
426 {
427 /* The command line argument: -d is used to determine if the test is being run
428 in debug mode. The regress tool requires only one line output:PASS or FAIL.
429 All of the printfs associated with this test has been handled with a if (debug_mode)
430 test.
431 Command line argument -l <num> sets the number of loops.
432 Command line argument -c <num> sets the number of cpus.
433 Usage: lock [-d] [-l <num>] [-c <num>]
434 */
435 PLOptStatus os;
436 PLOptState *opt = PL_CreateOptState(argc, argv, "dvl:c:");
437 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
438 {
439 if (PL_OPT_BAD == os) continue;
440 switch (opt->option)
441 {
442 case 'd': /* debug mode */
443 debug_mode = PR_TRUE;
444 break;
445 case 'v': /* debug mode */
446 verbosity = PR_TRUE;
447 break;
448 case 'l': /* number of loops */
449 loops = atoi(opt->value);
450 break;
451 case 'c': /* number of cpus */
452 cpus = atoi(opt->value);
453 break;
454 default:
455 break;
456 }
457 }
458 PL_DestroyOptState(opt);
459 }
460
461 /* main test */
462 PR_SetConcurrency(8);
463
464 if (loops == 0) loops = 100;
465 if (debug_mode)
466 {
467 std_err = PR_STDERR;
468 PR_fprintf(std_err, "Lock: Using %d loops\n", loops);
469 }
470
471 if (cpus == 0) cpus = 2;
472 if (debug_mode) PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus);
473
474 (void)Sleeper(10); /* try filling in the caches */
475
476 for (cpu = 1; cpu <= cpus; ++cpu)
477 {
478 if (debug_mode) PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu);
479 PR_SetConcurrency(cpu);
480
481 duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0);
482 duration = 0;
483
484 (void)Test("Lock creation/deletion", MakeLock, loops, 0);
485 (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock, loops, 0);
486 (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops, duration);
487 (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0);
488 (void)Test("Monitor non-contentious locking/unlocking", NonContentiousMonitor, loops, 0);
489 (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor, loops, duration);
490
491 (void)Test("Cached monitor non-contentious locking/unlocking", NonContentiousCMonitor, loops, 0);
492 (void)Test("Cached monitor contentious locking/unlocking", ContentiousCMonitor, loops, duration);
493
494 (void)ReentrantMonitor(loops);
495 }
496
497 if (debug_mode)
498 PR_fprintf(
499 std_err, "%s: test %s\n", "Lock(mutex) test",
500 ((rv) ? "passed" : "failed"));
501 else {
502 if (!rv)
503 failed_already=1;
504 }
505
506 if(failed_already)
507 {
508 PR_fprintf(PR_STDOUT, "FAIL\n");
509 return 1;
510 }
511 else
512 {
513 PR_fprintf(PR_STDOUT, "PASS\n");
514 return 0;
515 }
516
517 } /* main */
518
519 /* testlock.c */
520