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) {
215 PR_fprintf(std_err, "Reentrant thread created\n");
216 }
217 PR_EnterMonitor(ml);
218 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
219 if (debug_mode) {
220 PR_fprintf(std_err, "Reentrant thread acquired monitor\n");
221 }
222 PR_ExitMonitor(ml);
223 if (debug_mode) {
224 PR_fprintf(std_err, "Reentrant thread released monitor\n");
225 }
226 } /* TryEntry */
227
ReentrantMonitor(PRUint32 loops)228 static PRIntervalTime ReentrantMonitor(PRUint32 loops)
229 {
230 PRStatus status;
231 PRThread *thread;
232 PRMonitor *ml = PR_NewMonitor();
233 if (debug_mode) {
234 PR_fprintf(std_err, "\nMonitor created for reentrant test\n");
235 }
236
237 PR_EnterMonitor(ml);
238 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
239 PR_EnterMonitor(ml);
240 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
241 if (debug_mode) {
242 PR_fprintf(std_err, "Monitor acquired twice\n");
243 }
244
245 thread = PR_CreateThread(
246 PR_USER_THREAD, TryEntry, ml,
247 PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
248 PR_ASSERT(thread != NULL);
249 PR_Sleep(PR_SecondsToInterval(1));
250 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
251
252 PR_ExitMonitor(ml);
253 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
254 if (debug_mode) {
255 PR_fprintf(std_err, "Monitor released first time\n");
256 }
257
258 PR_ExitMonitor(ml);
259 if (debug_mode) {
260 PR_fprintf(std_err, "Monitor released second time\n");
261 }
262
263 status = PR_JoinThread(thread);
264 if (debug_mode) PR_fprintf(std_err,
265 "Reentrant thread joined %s\n",
266 (status == PR_SUCCESS) ? "successfully" : "in error");
267
268 PR_DestroyMonitor(ml);
269 return 0;
270 } /* ReentrantMonitor */
271
MonitorContender(void * arg)272 static void PR_CALLBACK MonitorContender(void *arg)
273 {
274 MonitorContentious_t *contention = (MonitorContentious_t*)arg;
275 while (contention->loops-- > 0)
276 {
277 PR_EnterMonitor(contention->ml);
278 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
279 contention->contender+= 1;
280 contention->overhead += contention->interval;
281 PR_Sleep(contention->interval);
282 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
283 PR_ExitMonitor(contention->ml);
284 }
285 } /* MonitorContender */
286
ContentiousMonitor(PRUint32 loops)287 static PRUint32 ContentiousMonitor(PRUint32 loops)
288 {
289 PRStatus status;
290 PRThread *thread = NULL;
291 MonitorContentious_t * contention;
292 PRIntervalTime rv, overhead, timein = PR_IntervalNow();
293
294 contention = PR_NEWZAP(MonitorContentious_t);
295 contention->loops = loops;
296 contention->overhead = 0;
297 contention->ml = PR_NewMonitor();
298 contention->interval = contention_interval;
299 thread = PR_CreateThread(
300 PR_USER_THREAD, MonitorContender, contention,
301 PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
302 PR_ASSERT(thread != NULL);
303
304 overhead = PR_IntervalNow() - timein;
305
306 while (contention->loops-- > 0)
307 {
308 PR_EnterMonitor(contention->ml);
309 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
310 contention->contentious+= 1;
311 contention->overhead += contention->interval;
312 PR_Sleep(contention->interval);
313 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
314 PR_ExitMonitor(contention->ml);
315 }
316
317 timein = PR_IntervalNow();
318 status = PR_JoinThread(thread);
319 PR_DestroyMonitor(contention->ml);
320 overhead += (PR_IntervalNow() - timein);
321 rv = overhead + contention->overhead;
322 if (verbosity)
323 PR_fprintf(
324 std_err, "Access ratio: %u to %u\n",
325 contention->contentious, contention->contender);
326 PR_Free(contention);
327 return rv;
328 } /* ContentiousMonitor */
329
330 /*
331 ** CACHED MONITORS
332 */
NonContentiousCMonitor(PRUint32 loops)333 static PRIntervalTime NonContentiousCMonitor(PRUint32 loops)
334 {
335 MonitorContentious_t contention;
336 while (loops-- > 0)
337 {
338 PR_CEnterMonitor(&contention);
339 PR_CExitMonitor(&contention);
340 }
341 return 0;
342 } /* NonContentiousCMonitor */
343
Contender(void * arg)344 static void PR_CALLBACK Contender(void *arg)
345 {
346 MonitorContentious_t *contention = (MonitorContentious_t*)arg;
347 while (contention->loops-- > 0)
348 {
349 PR_CEnterMonitor(contention);
350 contention->contender+= 1;
351 contention->overhead += contention->interval;
352 PR_Sleep(contention->interval);
353 PR_CExitMonitor(contention);
354 }
355 } /* Contender */
356
ContentiousCMonitor(PRUint32 loops)357 static PRIntervalTime ContentiousCMonitor(PRUint32 loops)
358 {
359 PRStatus status;
360 PRThread *thread = NULL;
361 MonitorContentious_t * contention;
362 PRIntervalTime overhead, timein = PR_IntervalNow();
363
364 contention = PR_NEWZAP(MonitorContentious_t);
365 contention->ml = NULL;
366 contention->loops = loops;
367 contention->interval = contention_interval;
368 thread = PR_CreateThread(
369 PR_USER_THREAD, Contender, contention,
370 PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
371 PR_ASSERT(thread != NULL);
372
373 overhead = PR_IntervalNow() - timein;
374
375 while (contention->loops-- > 0)
376 {
377 PR_CEnterMonitor(contention);
378 contention->contentious+= 1;
379 contention->overhead += contention->interval;
380 PR_Sleep(contention->interval);
381 PR_CExitMonitor(contention);
382 }
383
384 timein = PR_IntervalNow();
385 status = PR_JoinThread(thread);
386 overhead += (PR_IntervalNow() - timein);
387 overhead += overhead + contention->overhead;
388 if (verbosity)
389 PR_fprintf(
390 std_err, "Access ratio: %u to %u\n",
391 contention->contentious, contention->contender);
392 PR_Free(contention);
393 return overhead;
394 } /* ContentiousCMonitor */
395
Test(const char * msg,PRUint32 (* test)(PRUint32 loops),PRUint32 loops,PRIntervalTime overhead)396 static PRIntervalTime Test(
397 const char* msg, PRUint32 (*test)(PRUint32 loops),
398 PRUint32 loops, PRIntervalTime overhead)
399 {
400 /*
401 * overhead - overhead not measured by the test.
402 * duration - wall clock time it took to perform test.
403 * predicted - extra time test says should not be counted
404 *
405 * Time accountable to the test is duration - overhead - predicted
406 * All times are Intervals and accumulated for all iterations.
407 */
408 PRFloat64 elapsed;
409 PRIntervalTime accountable, duration;
410 PRUintn spaces = PL_strlen(msg);
411 PRIntervalTime timeout, timein = PR_IntervalNow();
412 PRIntervalTime predicted = test(loops);
413 timeout = PR_IntervalNow();
414 duration = timeout - timein;
415
416 if (debug_mode)
417 {
418 accountable = duration - predicted;
419 accountable -= overhead;
420 elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable);
421 PR_fprintf(PR_STDOUT, "%s:", msg);
422 while (spaces++ < 50) {
423 PR_fprintf(PR_STDOUT, " ");
424 }
425 if ((PRInt32)accountable < 0) {
426 PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n");
427 }
428 else {
429 PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed/loops);
430 }
431 }
432 return duration;
433 } /* Test */
434
main(int argc,char ** argv)435 int main(int argc, char **argv)
436 {
437 PRBool rv = PR_TRUE;
438 PRIntervalTime duration;
439 PRUint32 cpu, cpus = 2, loops = 100;
440
441
442 PR_STDIO_INIT();
443 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
444 {
445 /* The command line argument: -d is used to determine if the test is being run
446 in debug mode. The regress tool requires only one line output:PASS or FAIL.
447 All of the printfs associated with this test has been handled with a if (debug_mode)
448 test.
449 Command line argument -l <num> sets the number of loops.
450 Command line argument -c <num> sets the number of cpus.
451 Usage: lock [-d] [-l <num>] [-c <num>]
452 */
453 PLOptStatus os;
454 PLOptState *opt = PL_CreateOptState(argc, argv, "dvl:c:");
455 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
456 {
457 if (PL_OPT_BAD == os) {
458 continue;
459 }
460 switch (opt->option)
461 {
462 case 'd': /* debug mode */
463 debug_mode = PR_TRUE;
464 break;
465 case 'v': /* debug mode */
466 verbosity = PR_TRUE;
467 break;
468 case 'l': /* number of loops */
469 loops = atoi(opt->value);
470 break;
471 case 'c': /* number of cpus */
472 cpus = atoi(opt->value);
473 break;
474 default:
475 break;
476 }
477 }
478 PL_DestroyOptState(opt);
479 }
480
481 /* main test */
482 PR_SetConcurrency(8);
483
484 if (loops == 0) {
485 loops = 100;
486 }
487 if (debug_mode)
488 {
489 std_err = PR_STDERR;
490 PR_fprintf(std_err, "Lock: Using %d loops\n", loops);
491 }
492
493 if (cpus == 0) {
494 cpus = 2;
495 }
496 if (debug_mode) {
497 PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus);
498 }
499
500 (void)Sleeper(10); /* try filling in the caches */
501
502 for (cpu = 1; cpu <= cpus; ++cpu)
503 {
504 if (debug_mode) {
505 PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu);
506 }
507 PR_SetConcurrency(cpu);
508
509 duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0);
510 duration = 0;
511
512 (void)Test("Lock creation/deletion", MakeLock, loops, 0);
513 (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock, loops, 0);
514 (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops, duration);
515 (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0);
516 (void)Test("Monitor non-contentious locking/unlocking", NonContentiousMonitor, loops, 0);
517 (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor, loops, duration);
518
519 (void)Test("Cached monitor non-contentious locking/unlocking", NonContentiousCMonitor, loops, 0);
520 (void)Test("Cached monitor contentious locking/unlocking", ContentiousCMonitor, loops, duration);
521
522 (void)ReentrantMonitor(loops);
523 }
524
525 if (debug_mode)
526 PR_fprintf(
527 std_err, "%s: test %s\n", "Lock(mutex) test",
528 ((rv) ? "passed" : "failed"));
529 else {
530 if (!rv) {
531 failed_already=1;
532 }
533 }
534
535 if(failed_already)
536 {
537 PR_fprintf(PR_STDOUT, "FAIL\n");
538 return 1;
539 }
540 else
541 {
542 PR_fprintf(PR_STDOUT, "PASS\n");
543 return 0;
544 }
545
546 } /* main */
547
548 /* testlock.c */
549