1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/tests/lock.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,519 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 +** File: lock.c 1.11 +** Purpose: test basic locking functions 1.12 +** 1.13 +** Modification History: 1.14 +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. 1.15 +** The debug mode will print all of the printfs associated with this test. 1.16 +** The regress mode will be the default mode. Since the regress tool limits 1.17 +** the output to a one line status:PASS or FAIL,all of the printf statements 1.18 +** have been handled with an if (debug_mode) statement. 1.19 +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to 1.20 +** recognize the return code from tha main program. 1.21 +** 1.22 +** 11-Aug-97 LarryH. Win16 port of NSPR. 1.23 +** - Added "PASS", "FAIL" messages on completion. 1.24 +** - Change stack variables to static scope variables 1.25 +** because of shadow-stack use by Win16 1.26 +** - Added PR_CALLBACK attribute to functions called by NSPR 1.27 +** - Added command line arguments: 1.28 +** - l <num> to control the number of loops 1.29 +** - c <num> to control the number of CPUs. 1.30 +** (was positional argv). 1.31 +** 1.32 +** 1.33 +***********************************************************************/ 1.34 + 1.35 +/*********************************************************************** 1.36 +** Includes 1.37 +***********************************************************************/ 1.38 +/* Used to get the command line option */ 1.39 +#include "plgetopt.h" 1.40 + 1.41 +#include "prio.h" 1.42 +#include "prcmon.h" 1.43 +#include "prinit.h" 1.44 +#include "prinrval.h" 1.45 +#include "prprf.h" 1.46 +#include "prlock.h" 1.47 +#include "prlog.h" 1.48 +#include "prmon.h" 1.49 +#include "prmem.h" 1.50 +#include "prthread.h" 1.51 +#include "prtypes.h" 1.52 + 1.53 +#include "plstr.h" 1.54 + 1.55 +#include <stdlib.h> 1.56 + 1.57 +#if defined(XP_UNIX) 1.58 +#include <string.h> 1.59 +#endif 1.60 + 1.61 +static PRIntn failed_already=0; 1.62 +static PRFileDesc *std_err = NULL; 1.63 +static PRBool verbosity = PR_FALSE; 1.64 +static PRBool debug_mode = PR_FALSE; 1.65 + 1.66 +const static PRIntervalTime contention_interval = 50; 1.67 + 1.68 +typedef struct LockContentious_s { 1.69 + PRLock *ml; 1.70 + PRInt32 loops; 1.71 + PRUint32 contender; 1.72 + PRUint32 contentious; 1.73 + PRIntervalTime overhead; 1.74 + PRIntervalTime interval; 1.75 +} LockContentious_t; 1.76 + 1.77 +typedef struct MonitorContentious_s { 1.78 + PRMonitor *ml; 1.79 + PRInt32 loops; 1.80 + PRUint32 contender; 1.81 + PRUint32 contentious; 1.82 + PRIntervalTime overhead; 1.83 + PRIntervalTime interval; 1.84 +} MonitorContentious_t; 1.85 + 1.86 + 1.87 +static PRIntervalTime Sleeper(PRUint32 loops) 1.88 +{ 1.89 + PRIntervalTime predicted = 0; 1.90 + while (loops-- > 0) 1.91 + { 1.92 + predicted += contention_interval; 1.93 + (void)PR_Sleep(contention_interval); 1.94 + } 1.95 + return predicted; 1.96 +} /* Sleeper */ 1.97 + 1.98 +/* 1.99 +** BASIC LOCKS 1.100 +*/ 1.101 +static PRIntervalTime MakeLock(PRUint32 loops) 1.102 +{ 1.103 + PRLock *ml = NULL; 1.104 + while (loops-- > 0) 1.105 + { 1.106 + ml = PR_NewLock(); 1.107 + PR_DestroyLock(ml); 1.108 + ml = NULL; 1.109 + } 1.110 + return 0; 1.111 +} /* MakeLock */ 1.112 + 1.113 +static PRIntervalTime NonContentiousLock(PRUint32 loops) 1.114 +{ 1.115 + PRLock *ml = NULL; 1.116 + ml = PR_NewLock(); 1.117 + while (loops-- > 0) 1.118 + { 1.119 + PR_Lock(ml); 1.120 + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(ml); 1.121 + PR_Unlock(ml); 1.122 + } 1.123 + PR_DestroyLock(ml); 1.124 + return 0; 1.125 +} /* NonContentiousLock */ 1.126 + 1.127 +static void PR_CALLBACK LockContender(void *arg) 1.128 +{ 1.129 + LockContentious_t *contention = (LockContentious_t*)arg; 1.130 + while (contention->loops-- > 0) 1.131 + { 1.132 + PR_Lock(contention->ml); 1.133 + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); 1.134 + contention->contender+= 1; 1.135 + contention->overhead += contention->interval; 1.136 + PR_Sleep(contention->interval); 1.137 + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); 1.138 + PR_Unlock(contention->ml); 1.139 + } 1.140 +} /* LockContender */ 1.141 + 1.142 +static PRIntervalTime ContentiousLock(PRUint32 loops) 1.143 +{ 1.144 + PRStatus status; 1.145 + PRThread *thread = NULL; 1.146 + LockContentious_t * contention; 1.147 + PRIntervalTime rv, overhead, timein = PR_IntervalNow(); 1.148 + 1.149 + contention = PR_NEWZAP(LockContentious_t); 1.150 + contention->loops = loops; 1.151 + contention->overhead = 0; 1.152 + contention->ml = PR_NewLock(); 1.153 + contention->interval = contention_interval; 1.154 + thread = PR_CreateThread( 1.155 + PR_USER_THREAD, LockContender, contention, 1.156 + PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); 1.157 + PR_ASSERT(thread != NULL); 1.158 + 1.159 + overhead = PR_IntervalNow() - timein; 1.160 + 1.161 + while (contention->loops-- > 0) 1.162 + { 1.163 + PR_Lock(contention->ml); 1.164 + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); 1.165 + contention->contentious+= 1; 1.166 + contention->overhead += contention->interval; 1.167 + PR_Sleep(contention->interval); 1.168 + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); 1.169 + PR_Unlock(contention->ml); 1.170 + } 1.171 + 1.172 + timein = PR_IntervalNow(); 1.173 + status = PR_JoinThread(thread); 1.174 + PR_DestroyLock(contention->ml); 1.175 + overhead += (PR_IntervalNow() - timein); 1.176 + rv = overhead + contention->overhead; 1.177 + if (verbosity) 1.178 + PR_fprintf( 1.179 + std_err, "Access ratio: %u to %u\n", 1.180 + contention->contentious, contention->contender); 1.181 + PR_Free(contention); 1.182 + return rv; 1.183 +} /* ContentiousLock */ 1.184 + 1.185 +/* 1.186 +** MONITORS 1.187 +*/ 1.188 +static PRIntervalTime MakeMonitor(PRUint32 loops) 1.189 +{ 1.190 + PRMonitor *ml = NULL; 1.191 + while (loops-- > 0) 1.192 + { 1.193 + ml = PR_NewMonitor(); 1.194 + PR_DestroyMonitor(ml); 1.195 + ml = NULL; 1.196 + } 1.197 + return 0; 1.198 +} /* MakeMonitor */ 1.199 + 1.200 +static PRIntervalTime NonContentiousMonitor(PRUint32 loops) 1.201 +{ 1.202 + PRMonitor *ml = NULL; 1.203 + ml = PR_NewMonitor(); 1.204 + while (loops-- > 0) 1.205 + { 1.206 + PR_EnterMonitor(ml); 1.207 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); 1.208 + PR_ExitMonitor(ml); 1.209 + } 1.210 + PR_DestroyMonitor(ml); 1.211 + return 0; 1.212 +} /* NonContentiousMonitor */ 1.213 + 1.214 +static void PR_CALLBACK TryEntry(void *arg) 1.215 +{ 1.216 + PRMonitor *ml = (PRMonitor*)arg; 1.217 + if (debug_mode) PR_fprintf(std_err, "Reentrant thread created\n"); 1.218 + PR_EnterMonitor(ml); 1.219 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); 1.220 + if (debug_mode) PR_fprintf(std_err, "Reentrant thread acquired monitor\n"); 1.221 + PR_ExitMonitor(ml); 1.222 + if (debug_mode) PR_fprintf(std_err, "Reentrant thread released monitor\n"); 1.223 +} /* TryEntry */ 1.224 + 1.225 +static PRIntervalTime ReentrantMonitor(PRUint32 loops) 1.226 +{ 1.227 + PRStatus status; 1.228 + PRThread *thread; 1.229 + PRMonitor *ml = PR_NewMonitor(); 1.230 + if (debug_mode) PR_fprintf(std_err, "\nMonitor created for reentrant test\n"); 1.231 + 1.232 + PR_EnterMonitor(ml); 1.233 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); 1.234 + PR_EnterMonitor(ml); 1.235 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); 1.236 + if (debug_mode) PR_fprintf(std_err, "Monitor acquired twice\n"); 1.237 + 1.238 + thread = PR_CreateThread( 1.239 + PR_USER_THREAD, TryEntry, ml, 1.240 + PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); 1.241 + PR_ASSERT(thread != NULL); 1.242 + PR_Sleep(PR_SecondsToInterval(1)); 1.243 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); 1.244 + 1.245 + PR_ExitMonitor(ml); 1.246 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); 1.247 + if (debug_mode) PR_fprintf(std_err, "Monitor released first time\n"); 1.248 + 1.249 + PR_ExitMonitor(ml); 1.250 + if (debug_mode) PR_fprintf(std_err, "Monitor released second time\n"); 1.251 + 1.252 + status = PR_JoinThread(thread); 1.253 + if (debug_mode) PR_fprintf(std_err, 1.254 + "Reentrant thread joined %s\n", 1.255 + (status == PR_SUCCESS) ? "successfully" : "in error"); 1.256 + 1.257 + PR_DestroyMonitor(ml); 1.258 + return 0; 1.259 +} /* ReentrantMonitor */ 1.260 + 1.261 +static void PR_CALLBACK MonitorContender(void *arg) 1.262 +{ 1.263 + MonitorContentious_t *contention = (MonitorContentious_t*)arg; 1.264 + while (contention->loops-- > 0) 1.265 + { 1.266 + PR_EnterMonitor(contention->ml); 1.267 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); 1.268 + contention->contender+= 1; 1.269 + contention->overhead += contention->interval; 1.270 + PR_Sleep(contention->interval); 1.271 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); 1.272 + PR_ExitMonitor(contention->ml); 1.273 + } 1.274 +} /* MonitorContender */ 1.275 + 1.276 +static PRUint32 ContentiousMonitor(PRUint32 loops) 1.277 +{ 1.278 + PRStatus status; 1.279 + PRThread *thread = NULL; 1.280 + MonitorContentious_t * contention; 1.281 + PRIntervalTime rv, overhead, timein = PR_IntervalNow(); 1.282 + 1.283 + contention = PR_NEWZAP(MonitorContentious_t); 1.284 + contention->loops = loops; 1.285 + contention->overhead = 0; 1.286 + contention->ml = PR_NewMonitor(); 1.287 + contention->interval = contention_interval; 1.288 + thread = PR_CreateThread( 1.289 + PR_USER_THREAD, MonitorContender, contention, 1.290 + PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); 1.291 + PR_ASSERT(thread != NULL); 1.292 + 1.293 + overhead = PR_IntervalNow() - timein; 1.294 + 1.295 + while (contention->loops-- > 0) 1.296 + { 1.297 + PR_EnterMonitor(contention->ml); 1.298 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); 1.299 + contention->contentious+= 1; 1.300 + contention->overhead += contention->interval; 1.301 + PR_Sleep(contention->interval); 1.302 + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); 1.303 + PR_ExitMonitor(contention->ml); 1.304 + } 1.305 + 1.306 + timein = PR_IntervalNow(); 1.307 + status = PR_JoinThread(thread); 1.308 + PR_DestroyMonitor(contention->ml); 1.309 + overhead += (PR_IntervalNow() - timein); 1.310 + rv = overhead + contention->overhead; 1.311 + if (verbosity) 1.312 + PR_fprintf( 1.313 + std_err, "Access ratio: %u to %u\n", 1.314 + contention->contentious, contention->contender); 1.315 + PR_Free(contention); 1.316 + return rv; 1.317 +} /* ContentiousMonitor */ 1.318 + 1.319 +/* 1.320 +** CACHED MONITORS 1.321 +*/ 1.322 +static PRIntervalTime NonContentiousCMonitor(PRUint32 loops) 1.323 +{ 1.324 + MonitorContentious_t contention; 1.325 + while (loops-- > 0) 1.326 + { 1.327 + PR_CEnterMonitor(&contention); 1.328 + PR_CExitMonitor(&contention); 1.329 + } 1.330 + return 0; 1.331 +} /* NonContentiousCMonitor */ 1.332 + 1.333 +static void PR_CALLBACK Contender(void *arg) 1.334 +{ 1.335 + MonitorContentious_t *contention = (MonitorContentious_t*)arg; 1.336 + while (contention->loops-- > 0) 1.337 + { 1.338 + PR_CEnterMonitor(contention); 1.339 + contention->contender+= 1; 1.340 + contention->overhead += contention->interval; 1.341 + PR_Sleep(contention->interval); 1.342 + PR_CExitMonitor(contention); 1.343 + } 1.344 +} /* Contender */ 1.345 + 1.346 +static PRIntervalTime ContentiousCMonitor(PRUint32 loops) 1.347 +{ 1.348 + PRStatus status; 1.349 + PRThread *thread = NULL; 1.350 + MonitorContentious_t * contention; 1.351 + PRIntervalTime overhead, timein = PR_IntervalNow(); 1.352 + 1.353 + contention = PR_NEWZAP(MonitorContentious_t); 1.354 + contention->ml = NULL; 1.355 + contention->loops = loops; 1.356 + contention->interval = contention_interval; 1.357 + thread = PR_CreateThread( 1.358 + PR_USER_THREAD, Contender, contention, 1.359 + PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); 1.360 + PR_ASSERT(thread != NULL); 1.361 + 1.362 + overhead = PR_IntervalNow() - timein; 1.363 + 1.364 + while (contention->loops-- > 0) 1.365 + { 1.366 + PR_CEnterMonitor(contention); 1.367 + contention->contentious+= 1; 1.368 + contention->overhead += contention->interval; 1.369 + PR_Sleep(contention->interval); 1.370 + PR_CExitMonitor(contention); 1.371 + } 1.372 + 1.373 + timein = PR_IntervalNow(); 1.374 + status = PR_JoinThread(thread); 1.375 + overhead += (PR_IntervalNow() - timein); 1.376 + overhead += overhead + contention->overhead; 1.377 + if (verbosity) 1.378 + PR_fprintf( 1.379 + std_err, "Access ratio: %u to %u\n", 1.380 + contention->contentious, contention->contender); 1.381 + PR_Free(contention); 1.382 + return overhead; 1.383 +} /* ContentiousCMonitor */ 1.384 + 1.385 +static PRIntervalTime Test( 1.386 + const char* msg, PRUint32 (*test)(PRUint32 loops), 1.387 + PRUint32 loops, PRIntervalTime overhead) 1.388 +{ 1.389 + /* 1.390 + * overhead - overhead not measured by the test. 1.391 + * duration - wall clock time it took to perform test. 1.392 + * predicted - extra time test says should not be counted 1.393 + * 1.394 + * Time accountable to the test is duration - overhead - predicted 1.395 + * All times are Intervals and accumulated for all iterations. 1.396 + */ 1.397 + PRFloat64 elapsed; 1.398 + PRIntervalTime accountable, duration; 1.399 + PRUintn spaces = PL_strlen(msg); 1.400 + PRIntervalTime timeout, timein = PR_IntervalNow(); 1.401 + PRIntervalTime predicted = test(loops); 1.402 + timeout = PR_IntervalNow(); 1.403 + duration = timeout - timein; 1.404 + 1.405 + if (debug_mode) 1.406 + { 1.407 + accountable = duration - predicted; 1.408 + accountable -= overhead; 1.409 + elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable); 1.410 + PR_fprintf(PR_STDOUT, "%s:", msg); 1.411 + while (spaces++ < 50) PR_fprintf(PR_STDOUT, " "); 1.412 + if ((PRInt32)accountable < 0) 1.413 + PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n"); 1.414 + else 1.415 + PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed/loops); 1.416 + } 1.417 + return duration; 1.418 +} /* Test */ 1.419 + 1.420 +int main(int argc, char **argv) 1.421 +{ 1.422 + PRBool rv = PR_TRUE; 1.423 + PRIntervalTime duration; 1.424 + PRUint32 cpu, cpus = 2, loops = 100; 1.425 + 1.426 + 1.427 + PR_STDIO_INIT(); 1.428 + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 1.429 + { 1.430 + /* The command line argument: -d is used to determine if the test is being run 1.431 + in debug mode. The regress tool requires only one line output:PASS or FAIL. 1.432 + All of the printfs associated with this test has been handled with a if (debug_mode) 1.433 + test. 1.434 + Command line argument -l <num> sets the number of loops. 1.435 + Command line argument -c <num> sets the number of cpus. 1.436 + Usage: lock [-d] [-l <num>] [-c <num>] 1.437 + */ 1.438 + PLOptStatus os; 1.439 + PLOptState *opt = PL_CreateOptState(argc, argv, "dvl:c:"); 1.440 + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 1.441 + { 1.442 + if (PL_OPT_BAD == os) continue; 1.443 + switch (opt->option) 1.444 + { 1.445 + case 'd': /* debug mode */ 1.446 + debug_mode = PR_TRUE; 1.447 + break; 1.448 + case 'v': /* debug mode */ 1.449 + verbosity = PR_TRUE; 1.450 + break; 1.451 + case 'l': /* number of loops */ 1.452 + loops = atoi(opt->value); 1.453 + break; 1.454 + case 'c': /* number of cpus */ 1.455 + cpus = atoi(opt->value); 1.456 + break; 1.457 + default: 1.458 + break; 1.459 + } 1.460 + } 1.461 + PL_DestroyOptState(opt); 1.462 + } 1.463 + 1.464 + /* main test */ 1.465 + PR_SetConcurrency(8); 1.466 + 1.467 + if (loops == 0) loops = 100; 1.468 + if (debug_mode) 1.469 + { 1.470 + std_err = PR_STDERR; 1.471 + PR_fprintf(std_err, "Lock: Using %d loops\n", loops); 1.472 + } 1.473 + 1.474 + if (cpus == 0) cpus = 2; 1.475 + if (debug_mode) PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus); 1.476 + 1.477 + (void)Sleeper(10); /* try filling in the caches */ 1.478 + 1.479 + for (cpu = 1; cpu <= cpus; ++cpu) 1.480 + { 1.481 + if (debug_mode) PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu); 1.482 + PR_SetConcurrency(cpu); 1.483 + 1.484 + duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0); 1.485 + duration = 0; 1.486 + 1.487 + (void)Test("Lock creation/deletion", MakeLock, loops, 0); 1.488 + (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock, loops, 0); 1.489 + (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops, duration); 1.490 + (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0); 1.491 + (void)Test("Monitor non-contentious locking/unlocking", NonContentiousMonitor, loops, 0); 1.492 + (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor, loops, duration); 1.493 + 1.494 + (void)Test("Cached monitor non-contentious locking/unlocking", NonContentiousCMonitor, loops, 0); 1.495 + (void)Test("Cached monitor contentious locking/unlocking", ContentiousCMonitor, loops, duration); 1.496 + 1.497 + (void)ReentrantMonitor(loops); 1.498 + } 1.499 + 1.500 + if (debug_mode) 1.501 + PR_fprintf( 1.502 + std_err, "%s: test %s\n", "Lock(mutex) test", 1.503 + ((rv) ? "passed" : "failed")); 1.504 + else { 1.505 + if (!rv) 1.506 + failed_already=1; 1.507 + } 1.508 + 1.509 + if(failed_already) 1.510 + { 1.511 + PR_fprintf(PR_STDOUT, "FAIL\n"); 1.512 + return 1; 1.513 + } 1.514 + else 1.515 + { 1.516 + PR_fprintf(PR_STDOUT, "PASS\n"); 1.517 + return 0; 1.518 + } 1.519 + 1.520 +} /* main */ 1.521 + 1.522 +/* testlock.c */