/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* ** File: lock.c ** Purpose: test basic locking functions ** ** Modification History: ** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. ** The debug mode will print all of the printfs associated with this test. ** The regress mode will be the default mode. Since the regress tool limits ** the output to a one line status:PASS or FAIL,all of the printf statements ** have been handled with an if (debug_mode) statement. ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to ** recognize the return code from tha main program. ** ** 11-Aug-97 LarryH. Win16 port of NSPR. ** - Added "PASS", "FAIL" messages on completion. ** - Change stack variables to static scope variables ** because of shadow-stack use by Win16 ** - Added PR_CALLBACK attribute to functions called by NSPR ** - Added command line arguments: ** - l to control the number of loops ** - c to control the number of CPUs. ** (was positional argv). ** ** ***********************************************************************/ /*********************************************************************** ** Includes ***********************************************************************/ /* Used to get the command line option */ #include "plgetopt.h" #include "prio.h" #include "prcmon.h" #include "prinit.h" #include "prinrval.h" #include "prprf.h" #include "prlock.h" #include "prlog.h" #include "prmon.h" #include "prmem.h" #include "prthread.h" #include "prtypes.h" #include "plstr.h" #include #if defined(XP_UNIX) #include #endif static PRIntn failed_already=0; static PRFileDesc *std_err = NULL; static PRBool verbosity = PR_FALSE; static PRBool debug_mode = PR_FALSE; const static PRIntervalTime contention_interval = 50; typedef struct LockContentious_s { PRLock *ml; PRInt32 loops; PRUint32 contender; PRUint32 contentious; PRIntervalTime overhead; PRIntervalTime interval; } LockContentious_t; typedef struct MonitorContentious_s { PRMonitor *ml; PRInt32 loops; PRUint32 contender; PRUint32 contentious; PRIntervalTime overhead; PRIntervalTime interval; } MonitorContentious_t; static PRIntervalTime Sleeper(PRUint32 loops) { PRIntervalTime predicted = 0; while (loops-- > 0) { predicted += contention_interval; (void)PR_Sleep(contention_interval); } return predicted; } /* Sleeper */ /* ** BASIC LOCKS */ static PRIntervalTime MakeLock(PRUint32 loops) { PRLock *ml = NULL; while (loops-- > 0) { ml = PR_NewLock(); PR_DestroyLock(ml); ml = NULL; } return 0; } /* MakeLock */ static PRIntervalTime NonContentiousLock(PRUint32 loops) { PRLock *ml = NULL; ml = PR_NewLock(); while (loops-- > 0) { PR_Lock(ml); PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(ml); PR_Unlock(ml); } PR_DestroyLock(ml); return 0; } /* NonContentiousLock */ static void PR_CALLBACK LockContender(void *arg) { LockContentious_t *contention = (LockContentious_t*)arg; while (contention->loops-- > 0) { PR_Lock(contention->ml); PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); contention->contender+= 1; contention->overhead += contention->interval; PR_Sleep(contention->interval); PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); PR_Unlock(contention->ml); } } /* LockContender */ static PRIntervalTime ContentiousLock(PRUint32 loops) { PRStatus status; PRThread *thread = NULL; LockContentious_t * contention; PRIntervalTime rv, overhead, timein = PR_IntervalNow(); contention = PR_NEWZAP(LockContentious_t); contention->loops = loops; contention->overhead = 0; contention->ml = PR_NewLock(); contention->interval = contention_interval; thread = PR_CreateThread( PR_USER_THREAD, LockContender, contention, PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); PR_ASSERT(thread != NULL); overhead = PR_IntervalNow() - timein; while (contention->loops-- > 0) { PR_Lock(contention->ml); PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); contention->contentious+= 1; contention->overhead += contention->interval; PR_Sleep(contention->interval); PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); PR_Unlock(contention->ml); } timein = PR_IntervalNow(); status = PR_JoinThread(thread); PR_DestroyLock(contention->ml); overhead += (PR_IntervalNow() - timein); rv = overhead + contention->overhead; if (verbosity) PR_fprintf( std_err, "Access ratio: %u to %u\n", contention->contentious, contention->contender); PR_Free(contention); return rv; } /* ContentiousLock */ /* ** MONITORS */ static PRIntervalTime MakeMonitor(PRUint32 loops) { PRMonitor *ml = NULL; while (loops-- > 0) { ml = PR_NewMonitor(); PR_DestroyMonitor(ml); ml = NULL; } return 0; } /* MakeMonitor */ static PRIntervalTime NonContentiousMonitor(PRUint32 loops) { PRMonitor *ml = NULL; ml = PR_NewMonitor(); while (loops-- > 0) { PR_EnterMonitor(ml); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); PR_ExitMonitor(ml); } PR_DestroyMonitor(ml); return 0; } /* NonContentiousMonitor */ static void PR_CALLBACK TryEntry(void *arg) { PRMonitor *ml = (PRMonitor*)arg; if (debug_mode) { PR_fprintf(std_err, "Reentrant thread created\n"); } PR_EnterMonitor(ml); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); if (debug_mode) { PR_fprintf(std_err, "Reentrant thread acquired monitor\n"); } PR_ExitMonitor(ml); if (debug_mode) { PR_fprintf(std_err, "Reentrant thread released monitor\n"); } } /* TryEntry */ static PRIntervalTime ReentrantMonitor(PRUint32 loops) { PRStatus status; PRThread *thread; PRMonitor *ml = PR_NewMonitor(); if (debug_mode) { PR_fprintf(std_err, "\nMonitor created for reentrant test\n"); } PR_EnterMonitor(ml); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); PR_EnterMonitor(ml); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); if (debug_mode) { PR_fprintf(std_err, "Monitor acquired twice\n"); } thread = PR_CreateThread( PR_USER_THREAD, TryEntry, ml, PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); PR_ASSERT(thread != NULL); PR_Sleep(PR_SecondsToInterval(1)); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); PR_ExitMonitor(ml); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); if (debug_mode) { PR_fprintf(std_err, "Monitor released first time\n"); } PR_ExitMonitor(ml); if (debug_mode) { PR_fprintf(std_err, "Monitor released second time\n"); } status = PR_JoinThread(thread); if (debug_mode) PR_fprintf(std_err, "Reentrant thread joined %s\n", (status == PR_SUCCESS) ? "successfully" : "in error"); PR_DestroyMonitor(ml); return 0; } /* ReentrantMonitor */ static void PR_CALLBACK MonitorContender(void *arg) { MonitorContentious_t *contention = (MonitorContentious_t*)arg; while (contention->loops-- > 0) { PR_EnterMonitor(contention->ml); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); contention->contender+= 1; contention->overhead += contention->interval; PR_Sleep(contention->interval); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); PR_ExitMonitor(contention->ml); } } /* MonitorContender */ static PRUint32 ContentiousMonitor(PRUint32 loops) { PRStatus status; PRThread *thread = NULL; MonitorContentious_t * contention; PRIntervalTime rv, overhead, timein = PR_IntervalNow(); contention = PR_NEWZAP(MonitorContentious_t); contention->loops = loops; contention->overhead = 0; contention->ml = PR_NewMonitor(); contention->interval = contention_interval; thread = PR_CreateThread( PR_USER_THREAD, MonitorContender, contention, PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); PR_ASSERT(thread != NULL); overhead = PR_IntervalNow() - timein; while (contention->loops-- > 0) { PR_EnterMonitor(contention->ml); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); contention->contentious+= 1; contention->overhead += contention->interval; PR_Sleep(contention->interval); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); PR_ExitMonitor(contention->ml); } timein = PR_IntervalNow(); status = PR_JoinThread(thread); PR_DestroyMonitor(contention->ml); overhead += (PR_IntervalNow() - timein); rv = overhead + contention->overhead; if (verbosity) PR_fprintf( std_err, "Access ratio: %u to %u\n", contention->contentious, contention->contender); PR_Free(contention); return rv; } /* ContentiousMonitor */ /* ** CACHED MONITORS */ static PRIntervalTime NonContentiousCMonitor(PRUint32 loops) { MonitorContentious_t contention; while (loops-- > 0) { PR_CEnterMonitor(&contention); PR_CExitMonitor(&contention); } return 0; } /* NonContentiousCMonitor */ static void PR_CALLBACK Contender(void *arg) { MonitorContentious_t *contention = (MonitorContentious_t*)arg; while (contention->loops-- > 0) { PR_CEnterMonitor(contention); contention->contender+= 1; contention->overhead += contention->interval; PR_Sleep(contention->interval); PR_CExitMonitor(contention); } } /* Contender */ static PRIntervalTime ContentiousCMonitor(PRUint32 loops) { PRStatus status; PRThread *thread = NULL; MonitorContentious_t * contention; PRIntervalTime overhead, timein = PR_IntervalNow(); contention = PR_NEWZAP(MonitorContentious_t); contention->ml = NULL; contention->loops = loops; contention->interval = contention_interval; thread = PR_CreateThread( PR_USER_THREAD, Contender, contention, PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); PR_ASSERT(thread != NULL); overhead = PR_IntervalNow() - timein; while (contention->loops-- > 0) { PR_CEnterMonitor(contention); contention->contentious+= 1; contention->overhead += contention->interval; PR_Sleep(contention->interval); PR_CExitMonitor(contention); } timein = PR_IntervalNow(); status = PR_JoinThread(thread); overhead += (PR_IntervalNow() - timein); overhead += overhead + contention->overhead; if (verbosity) PR_fprintf( std_err, "Access ratio: %u to %u\n", contention->contentious, contention->contender); PR_Free(contention); return overhead; } /* ContentiousCMonitor */ static PRIntervalTime Test( const char* msg, PRUint32 (*test)(PRUint32 loops), PRUint32 loops, PRIntervalTime overhead) { /* * overhead - overhead not measured by the test. * duration - wall clock time it took to perform test. * predicted - extra time test says should not be counted * * Time accountable to the test is duration - overhead - predicted * All times are Intervals and accumulated for all iterations. */ PRFloat64 elapsed; PRIntervalTime accountable, duration; PRUintn spaces = PL_strlen(msg); PRIntervalTime timeout, timein = PR_IntervalNow(); PRIntervalTime predicted = test(loops); timeout = PR_IntervalNow(); duration = timeout - timein; if (debug_mode) { accountable = duration - predicted; accountable -= overhead; elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable); PR_fprintf(PR_STDOUT, "%s:", msg); while (spaces++ < 50) { PR_fprintf(PR_STDOUT, " "); } if ((PRInt32)accountable < 0) { PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n"); } else { PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed/loops); } } return duration; } /* Test */ int main(int argc, char **argv) { PRBool rv = PR_TRUE; PRIntervalTime duration; PRUint32 cpu, cpus = 2, loops = 100; PR_STDIO_INIT(); PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); { /* The command line argument: -d is used to determine if the test is being run in debug mode. The regress tool requires only one line output:PASS or FAIL. All of the printfs associated with this test has been handled with a if (debug_mode) test. Command line argument -l sets the number of loops. Command line argument -c sets the number of cpus. Usage: lock [-d] [-l ] [-c ] */ PLOptStatus os; PLOptState *opt = PL_CreateOptState(argc, argv, "dvl:c:"); while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { if (PL_OPT_BAD == os) { continue; } switch (opt->option) { case 'd': /* debug mode */ debug_mode = PR_TRUE; break; case 'v': /* debug mode */ verbosity = PR_TRUE; break; case 'l': /* number of loops */ loops = atoi(opt->value); break; case 'c': /* number of cpus */ cpus = atoi(opt->value); break; default: break; } } PL_DestroyOptState(opt); } /* main test */ PR_SetConcurrency(8); if (loops == 0) { loops = 100; } if (debug_mode) { std_err = PR_STDERR; PR_fprintf(std_err, "Lock: Using %d loops\n", loops); } if (cpus == 0) { cpus = 2; } if (debug_mode) { PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus); } (void)Sleeper(10); /* try filling in the caches */ for (cpu = 1; cpu <= cpus; ++cpu) { if (debug_mode) { PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu); } PR_SetConcurrency(cpu); duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0); duration = 0; (void)Test("Lock creation/deletion", MakeLock, loops, 0); (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock, loops, 0); (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops, duration); (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0); (void)Test("Monitor non-contentious locking/unlocking", NonContentiousMonitor, loops, 0); (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor, loops, duration); (void)Test("Cached monitor non-contentious locking/unlocking", NonContentiousCMonitor, loops, 0); (void)Test("Cached monitor contentious locking/unlocking", ContentiousCMonitor, loops, duration); (void)ReentrantMonitor(loops); } if (debug_mode) PR_fprintf( std_err, "%s: test %s\n", "Lock(mutex) test", ((rv) ? "passed" : "failed")); else { if (!rv) { failed_already=1; } } if(failed_already) { PR_fprintf(PR_STDOUT, "FAIL\n"); return 1; } else { PR_fprintf(PR_STDOUT, "PASS\n"); return 0; } } /* main */ /* testlock.c */