Back to index

lightning-sunbird  0.9+nobinonly
lock.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039 ** File:        lock.c
00040 ** Purpose:     test basic locking functions
00041 **
00042 ** Modification History:
00043 ** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
00044 **              The debug mode will print all of the printfs associated with this test.
00045 **                    The regress mode will be the default mode. Since the regress tool limits
00046 **           the output to a one line status:PASS or FAIL,all of the printf statements
00047 **                    have been handled with an if (debug_mode) statement.
00048 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
00049 **                   recognize the return code from tha main program.
00050 **
00051 ** 11-Aug-97 LarryH. Win16 port of NSPR.
00052 **           - Added "PASS", "FAIL" messages on completion.
00053 **           - Change stack variables to static scope variables
00054 **             because of shadow-stack use by Win16
00055 **           - Added PR_CALLBACK attribute to functions called by NSPR
00056 **           - Added command line arguments:
00057 **             - l <num> to control the number of loops
00058 **             - c <num> to control the number of CPUs.
00059 **             (was positional argv).
00060 ** 
00061 **
00062 ***********************************************************************/
00063 
00064 /***********************************************************************
00065 ** Includes
00066 ***********************************************************************/
00067 /* Used to get the command line option */
00068 #include "plgetopt.h"
00069 
00070 #include "prio.h"
00071 #include "prcmon.h"
00072 #include "prinit.h"
00073 #include "prinrval.h"
00074 #include "prprf.h"
00075 #include "prlock.h"
00076 #include "prlog.h"
00077 #include "prmon.h"
00078 #include "prmem.h"
00079 #include "prthread.h"
00080 #include "prtypes.h"
00081 
00082 #include "plstr.h"
00083 
00084 #include <stdlib.h>
00085 
00086 #if defined(XP_UNIX)
00087 #include <string.h>
00088 #endif
00089 
00090 #ifdef XP_MAC
00091 #include "prlog.h"
00092 #define printf PR_LogPrint
00093 extern void SetupMacPrintfLog(char *logFile);
00094 #endif
00095 
00096 static PRIntn failed_already=0;
00097 static PRFileDesc *std_err = NULL;
00098 static PRBool verbosity = PR_FALSE;
00099 static PRBool debug_mode = PR_FALSE;
00100 
00101 const static PRIntervalTime contention_interval = 50;
00102 
00103 typedef struct LockContentious_s {
00104     PRLock *ml;
00105     PRInt32 loops;
00106     PRUint32 contender;
00107     PRUint32 contentious;
00108     PRIntervalTime overhead;
00109     PRIntervalTime interval;
00110 } LockContentious_t;
00111 
00112 typedef struct MonitorContentious_s {
00113     PRMonitor *ml;
00114     PRInt32 loops;
00115     PRUint32 contender;
00116     PRUint32 contentious;
00117     PRIntervalTime overhead;
00118     PRIntervalTime interval;
00119 } MonitorContentious_t;
00120 
00121 
00122 static PRIntervalTime Sleeper(PRUint32 loops)
00123 {
00124     PRIntervalTime predicted = 0;
00125     while (loops-- > 0)
00126     {
00127         predicted += contention_interval;
00128         (void)PR_Sleep(contention_interval);
00129     }
00130     return predicted;
00131 }  /* Sleeper */
00132 
00133 /*
00134 ** BASIC LOCKS
00135 */
00136 static PRIntervalTime MakeLock(PRUint32 loops)
00137 {
00138     PRLock *ml = NULL;
00139     while (loops-- > 0)
00140     {
00141         ml = PR_NewLock();
00142         PR_DestroyLock(ml);
00143         ml = NULL;
00144     }
00145     return 0;
00146 }  /* MakeLock */
00147 
00148 static PRIntervalTime NonContentiousLock(PRUint32 loops)
00149 {
00150     PRLock *ml = NULL;
00151     ml = PR_NewLock();
00152     while (loops-- > 0)
00153     {
00154         PR_Lock(ml);
00155         PR_Unlock(ml);
00156     }
00157     PR_DestroyLock(ml);
00158     return 0;
00159 }  /* NonContentiousLock */
00160 
00161 static void PR_CALLBACK LockContender(void *arg)
00162 {
00163     LockContentious_t *contention = (LockContentious_t*)arg;
00164     while (contention->loops-- > 0)
00165     {
00166         PR_Lock(contention->ml);
00167         contention->contender+= 1;
00168         contention->overhead += contention->interval;
00169         PR_Sleep(contention->interval);
00170         PR_Unlock(contention->ml);
00171     }
00172 }  /* LockContender */
00173 
00174 static PRIntervalTime ContentiousLock(PRUint32 loops)
00175 {
00176     PRStatus status;
00177     PRThread *thread = NULL;
00178     LockContentious_t * contention;
00179     PRIntervalTime rv, overhead, timein = PR_IntervalNow();
00180 
00181     contention = PR_NEWZAP(LockContentious_t);
00182     contention->loops = loops;
00183     contention->overhead = 0;
00184     contention->ml = PR_NewLock();
00185     contention->interval = contention_interval;
00186     thread = PR_CreateThread(
00187         PR_USER_THREAD, LockContender, contention,
00188         PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
00189     PR_ASSERT(thread != NULL);
00190 
00191     overhead = PR_IntervalNow() - timein;
00192 
00193     while (contention->loops-- > 0)
00194     {
00195         PR_Lock(contention->ml);
00196         contention->contentious+= 1;
00197         contention->overhead += contention->interval;
00198         PR_Sleep(contention->interval);
00199         PR_Unlock(contention->ml);
00200     }
00201 
00202     timein = PR_IntervalNow();
00203     status = PR_JoinThread(thread);
00204     PR_DestroyLock(contention->ml);
00205     overhead += (PR_IntervalNow() - timein);
00206     rv = overhead + contention->overhead;
00207     if (verbosity)
00208         PR_fprintf(
00209             std_err, "Access ratio: %u to %u\n",
00210             contention->contentious, contention->contender);
00211     PR_Free(contention);
00212     return rv;
00213 }  /* ContentiousLock */
00214 
00215 /*
00216 ** MONITORS
00217 */
00218 static PRIntervalTime MakeMonitor(PRUint32 loops)
00219 {
00220     PRMonitor *ml = NULL;
00221     while (loops-- > 0)
00222     {
00223         ml = PR_NewMonitor();
00224         PR_DestroyMonitor(ml);
00225         ml = NULL;
00226     }
00227     return 0;
00228 }  /* MakeMonitor */
00229 
00230 static PRIntervalTime NonContentiousMonitor(PRUint32 loops)
00231 {
00232     PRMonitor *ml = NULL;
00233     ml = PR_NewMonitor();
00234     while (loops-- > 0)
00235     {
00236         PR_EnterMonitor(ml);
00237         PR_ExitMonitor(ml);
00238     }
00239     PR_DestroyMonitor(ml);
00240     return 0;
00241 }  /* NonContentiousMonitor */
00242 
00243 static void PR_CALLBACK TryEntry(void *arg)
00244 {
00245     PRMonitor *ml = (PRMonitor*)arg;
00246     if (debug_mode) PR_fprintf(std_err, "Reentrant thread created\n");
00247     PR_EnterMonitor(ml);
00248     if (debug_mode) PR_fprintf(std_err, "Reentrant thread acquired monitor\n");
00249     PR_ExitMonitor(ml);
00250     if (debug_mode) PR_fprintf(std_err, "Reentrant thread released monitor\n");
00251 }  /* TryEntry */
00252 
00253 static PRIntervalTime ReentrantMonitor(PRUint32 loops)
00254 {
00255     PRStatus status;
00256     PRThread *thread;
00257     PRMonitor *ml = PR_NewMonitor();
00258     if (debug_mode) PR_fprintf(std_err, "\nMonitor created for reentrant test\n");
00259 
00260     PR_EnterMonitor(ml);
00261     PR_EnterMonitor(ml);
00262     if (debug_mode) PR_fprintf(std_err, "Monitor acquired twice\n");
00263 
00264     thread = PR_CreateThread(
00265         PR_USER_THREAD, TryEntry, ml,
00266         PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
00267     PR_ASSERT(thread != NULL);
00268     PR_Sleep(PR_SecondsToInterval(1));
00269 
00270     PR_ExitMonitor(ml);
00271     if (debug_mode) PR_fprintf(std_err, "Monitor released first time\n");
00272 
00273     PR_ExitMonitor(ml);
00274     if (debug_mode) PR_fprintf(std_err, "Monitor released second time\n");
00275 
00276     status = PR_JoinThread(thread);
00277     if (debug_mode) PR_fprintf(std_err, 
00278         "Reentrant thread joined %s\n",
00279         (status == PR_SUCCESS) ? "successfully" : "in error");
00280 
00281     PR_DestroyMonitor(ml);
00282     return 0;
00283 }  /* ReentrantMonitor */
00284 
00285 static void PR_CALLBACK MonitorContender(void *arg)
00286 {
00287     MonitorContentious_t *contention = (MonitorContentious_t*)arg;
00288     while (contention->loops-- > 0)
00289     {
00290         PR_EnterMonitor(contention->ml);
00291         contention->contender+= 1;
00292         contention->overhead += contention->interval;
00293         PR_Sleep(contention->interval);
00294         PR_ExitMonitor(contention->ml);
00295     }
00296 }  /* MonitorContender */
00297 
00298 static PRUint32 ContentiousMonitor(PRUint32 loops)
00299 {
00300     PRStatus status;
00301     PRThread *thread = NULL;
00302     MonitorContentious_t * contention;
00303     PRIntervalTime rv, overhead, timein = PR_IntervalNow();
00304 
00305     contention = PR_NEWZAP(MonitorContentious_t);
00306     contention->loops = loops;
00307     contention->overhead = 0;
00308     contention->ml = PR_NewMonitor();
00309     contention->interval = contention_interval;
00310     thread = PR_CreateThread(
00311         PR_USER_THREAD, MonitorContender, contention,
00312         PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
00313     PR_ASSERT(thread != NULL);
00314 
00315     overhead = PR_IntervalNow() - timein;
00316 
00317     while (contention->loops-- > 0)
00318     {
00319         PR_EnterMonitor(contention->ml);
00320         contention->contentious+= 1;
00321         contention->overhead += contention->interval;
00322         PR_Sleep(contention->interval);
00323         PR_ExitMonitor(contention->ml);
00324     }
00325 
00326     timein = PR_IntervalNow();
00327     status = PR_JoinThread(thread);
00328     PR_DestroyMonitor(contention->ml);
00329     overhead += (PR_IntervalNow() - timein);
00330     rv = overhead + contention->overhead;
00331     if (verbosity)
00332         PR_fprintf(
00333             std_err, "Access ratio: %u to %u\n",
00334             contention->contentious, contention->contender);
00335     PR_Free(contention);
00336     return rv;
00337 }  /* ContentiousMonitor */
00338 
00339 /*
00340 ** CACHED MONITORS
00341 */
00342 static PRIntervalTime NonContentiousCMonitor(PRUint32 loops)
00343 {
00344     MonitorContentious_t contention;
00345     while (loops-- > 0)
00346     {
00347         PR_CEnterMonitor(&contention);
00348         PR_CExitMonitor(&contention);
00349     }
00350     return 0;
00351 }  /* NonContentiousCMonitor */
00352 
00353 static void PR_CALLBACK Contender(void *arg)
00354 {
00355     MonitorContentious_t *contention = (MonitorContentious_t*)arg;
00356     while (contention->loops-- > 0)
00357     {
00358         PR_CEnterMonitor(contention);
00359         contention->contender+= 1;
00360         contention->overhead += contention->interval;
00361         PR_Sleep(contention->interval);
00362         PR_CExitMonitor(contention);
00363     }
00364 }  /* Contender */
00365 
00366 static PRIntervalTime ContentiousCMonitor(PRUint32 loops)
00367 {
00368     PRStatus status;
00369     PRThread *thread = NULL;
00370     MonitorContentious_t * contention;
00371     PRIntervalTime overhead, timein = PR_IntervalNow();
00372 
00373     contention = PR_NEWZAP(MonitorContentious_t);
00374     contention->ml = NULL;
00375     contention->loops = loops;
00376     contention->interval = contention_interval;
00377     thread = PR_CreateThread(
00378         PR_USER_THREAD, Contender, contention,
00379         PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
00380     PR_ASSERT(thread != NULL);
00381 
00382     overhead = PR_IntervalNow() - timein;
00383 
00384     while (contention->loops-- > 0)
00385     {
00386         PR_CEnterMonitor(contention);
00387         contention->contentious+= 1;
00388         contention->overhead += contention->interval;
00389         PR_Sleep(contention->interval);
00390         PR_CExitMonitor(contention);
00391     }
00392 
00393     timein = PR_IntervalNow();
00394     status = PR_JoinThread(thread);
00395     overhead += (PR_IntervalNow() - timein);
00396     overhead += overhead + contention->overhead;
00397     if (verbosity)
00398         PR_fprintf(
00399             std_err, "Access ratio: %u to %u\n",
00400             contention->contentious, contention->contender);
00401     PR_Free(contention);
00402     return overhead;
00403 }  /* ContentiousCMonitor */
00404 
00405 static PRIntervalTime Test(
00406     const char* msg, PRUint32 (*test)(PRUint32 loops),
00407     PRUint32 loops, PRIntervalTime overhead)
00408 { 
00409     /*
00410      * overhead - overhead not measured by the test.
00411      * duration - wall clock time it took to perform test.
00412      * predicted - extra time test says should not be counted 
00413      *
00414      * Time accountable to the test is duration - overhead - predicted
00415      * All times are Intervals and accumulated for all iterations.
00416      */
00417     PRFloat64 elapsed;
00418     PRIntervalTime accountable, duration;    
00419     PRUintn spaces = PL_strlen(msg);
00420     PRIntervalTime timeout, timein = PR_IntervalNow();
00421     PRIntervalTime predicted = test(loops);
00422     timeout = PR_IntervalNow();
00423     duration = timeout - timein;
00424 
00425     if (debug_mode)
00426     {
00427         accountable = duration - predicted;
00428         accountable -= overhead;
00429         elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable);
00430         PR_fprintf(PR_STDOUT, "%s:", msg);
00431         while (spaces++ < 50) PR_fprintf(PR_STDOUT, " ");
00432         if ((PRInt32)accountable < 0)
00433             PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n");
00434         else
00435             PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed/loops);
00436     }
00437     return duration;
00438 }  /* Test */
00439 
00440 int main(int argc,  char **argv)
00441 {
00442     PRBool rv = PR_TRUE;
00443     PRIntervalTime duration;
00444     PRUint32 cpu, cpus = 2, loops = 100;
00445 
00446        
00447     PR_STDIO_INIT();
00448     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
00449     {
00450        /* The command line argument: -d is used to determine if the test is being run
00451        in debug mode. The regress tool requires only one line output:PASS or FAIL.
00452        All of the printfs associated with this test has been handled with a if (debug_mode)
00453        test.
00454         Command line argument -l <num> sets the number of loops.
00455         Command line argument -c <num> sets the number of cpus.
00456         Usage: lock [-d] [-l <num>] [-c <num>]
00457        */
00458        PLOptStatus os;
00459        PLOptState *opt = PL_CreateOptState(argc, argv, "dvl:c:");
00460        while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
00461         {
00462               if (PL_OPT_BAD == os) continue;
00463             switch (opt->option)
00464             {
00465             case 'd':  /* debug mode */
00466                      debug_mode = PR_TRUE;
00467                 break;
00468             case 'v':  /* debug mode */
00469                      verbosity = PR_TRUE;
00470                 break;
00471             case 'l':  /* number of loops */
00472                 loops = atoi(opt->value);
00473                 break;
00474             case 'c':  /* number of cpus */
00475                 cpus = atoi(opt->value);
00476                 break;
00477              default:
00478                 break;
00479             }
00480         }
00481        PL_DestroyOptState(opt);
00482     }
00483 
00484  /* main test */
00485     PR_SetConcurrency(8);
00486 
00487 #ifdef XP_MAC
00488        SetupMacPrintfLog("lock.log");
00489        debug_mode = 1;
00490 #endif
00491 
00492     if (loops == 0) loops = 100;
00493     if (debug_mode)
00494     {
00495         std_err = PR_STDERR;
00496         PR_fprintf(std_err, "Lock: Using %d loops\n", loops);
00497     }
00498 
00499     if (cpus == 0) cpus = 2;
00500     if (debug_mode) PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus);
00501 
00502     (void)Sleeper(10);  /* try filling in the caches */
00503 
00504     for (cpu = 1; cpu <= cpus; ++cpu)
00505     {
00506         if (debug_mode) PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu);
00507         PR_SetConcurrency(cpu);
00508 
00509         duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0);
00510         duration = 0;
00511 
00512         (void)Test("Lock creation/deletion", MakeLock, loops, 0);
00513         (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock, loops, 0);
00514         (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops, duration);
00515         (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0);
00516         (void)Test("Monitor non-contentious locking/unlocking", NonContentiousMonitor, loops, 0);
00517         (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor, loops, duration);
00518 
00519         (void)Test("Cached monitor non-contentious locking/unlocking", NonContentiousCMonitor, loops, 0);
00520         (void)Test("Cached monitor contentious locking/unlocking", ContentiousCMonitor, loops, duration);
00521 
00522         (void)ReentrantMonitor(loops);
00523     }
00524 
00525     if (debug_mode)
00526         PR_fprintf(
00527             std_err, "%s: test %s\n", "Lock(mutex) test",
00528             ((rv) ? "passed" : "failed"));
00529        else {
00530                if (!rv)
00531                       failed_already=1;
00532        }
00533 
00534        if(failed_already)   
00535        {
00536            PR_fprintf(PR_STDOUT, "FAIL\n"); 
00537               return 1;
00538     } 
00539        else
00540     {
00541            PR_fprintf(PR_STDOUT, "PASS\n"); 
00542               return 0;
00543     }
00544 
00545 }  /* main */
00546 
00547 /* testlock.c */