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