cvar.c (6903B)
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 ** 1996 - Netscape Communications Corporation 8 ** 9 ** Name: cvar.c 10 ** 11 ** Description: Tests Condition Variable Operations 12 ** 13 ** Modification History: 14 ** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. 15 ** The debug mode will print all of the printfs associated with this 16 *test. 17 ** The regress mode will be the default mode. Since the regress tool 18 *limits 19 ** the output to a one line status:PASS or FAIL,all of the printf 20 *statements 21 ** have been handled with an if (debug_mode) statement. 22 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been 23 *updated to 24 ** recognize the return code from tha main program. 25 ** 12-June-97 Revert to return code 0 and 1. 26 ***********************************************************************/ 27 28 /*********************************************************************** 29 ** Includes 30 ***********************************************************************/ 31 32 #include "nspr.h" 33 34 /* Used to get the command line option */ 35 #include "plgetopt.h" 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 PRMonitor* mon; 42 #define DEFAULT_COUNT 1000 43 PRInt32 count = 0; 44 PRIntn debug_mode; 45 46 #define kQSIZE 1 47 48 typedef struct { 49 PRLock* bufLock; 50 int startIdx; 51 int numFull; 52 PRCondVar* notFull; 53 PRCondVar* notEmpty; 54 void* data[kQSIZE]; 55 } CircBuf; 56 57 static PRBool failed = PR_FALSE; 58 59 /* 60 ** NewCB creates and initializes a new circular buffer. 61 */ 62 static CircBuf* NewCB(void) { 63 CircBuf* cbp; 64 65 cbp = PR_NEW(CircBuf); 66 if (cbp == NULL) { 67 return (NULL); 68 } 69 70 cbp->bufLock = PR_NewLock(); 71 cbp->startIdx = 0; 72 cbp->numFull = 0; 73 cbp->notFull = PR_NewCondVar(cbp->bufLock); 74 cbp->notEmpty = PR_NewCondVar(cbp->bufLock); 75 76 return (cbp); 77 } 78 79 /* 80 ** DeleteCB frees a circular buffer. 81 */ 82 static void DeleteCB(CircBuf* cbp) { 83 PR_DestroyLock(cbp->bufLock); 84 PR_DestroyCondVar(cbp->notFull); 85 PR_DestroyCondVar(cbp->notEmpty); 86 PR_DELETE(cbp); 87 } 88 89 /* 90 ** PutCBData puts new data on the queue. If the queue is full, it waits 91 ** until there is room. 92 */ 93 static void PutCBData(CircBuf* cbp, void* data) { 94 PR_Lock(cbp->bufLock); 95 /* wait while the buffer is full */ 96 while (cbp->numFull == kQSIZE) { 97 PR_WaitCondVar(cbp->notFull, PR_INTERVAL_NO_TIMEOUT); 98 } 99 cbp->data[(cbp->startIdx + cbp->numFull) % kQSIZE] = data; 100 cbp->numFull += 1; 101 102 /* let a waiting reader know that there is data */ 103 PR_NotifyCondVar(cbp->notEmpty); 104 PR_Unlock(cbp->bufLock); 105 } 106 107 /* 108 ** GetCBData gets the oldest data on the queue. If the queue is empty, it waits 109 ** until new data appears. 110 */ 111 static void* GetCBData(CircBuf* cbp) { 112 void* data; 113 114 PR_Lock(cbp->bufLock); 115 /* wait while the buffer is empty */ 116 while (cbp->numFull == 0) { 117 PR_WaitCondVar(cbp->notEmpty, PR_INTERVAL_NO_TIMEOUT); 118 } 119 data = cbp->data[cbp->startIdx]; 120 cbp->startIdx = (cbp->startIdx + 1) % kQSIZE; 121 cbp->numFull -= 1; 122 123 /* let a waiting writer know that there is room */ 124 PR_NotifyCondVar(cbp->notFull); 125 PR_Unlock(cbp->bufLock); 126 127 return (data); 128 } 129 130 /************************************************************************/ 131 132 static int alive; 133 134 static void PR_CALLBACK CXReader(void* arg) { 135 CircBuf* cbp = (CircBuf*)arg; 136 PRInt32 i, n; 137 void* data; 138 139 n = count / 2; 140 for (i = 0; i < n; i++) { 141 data = GetCBData(cbp); 142 if ((int)data != i) 143 if (debug_mode) { 144 printf("data mismatch at for i = %d usec\n", i); 145 } 146 } 147 148 PR_EnterMonitor(mon); 149 --alive; 150 PR_Notify(mon); 151 PR_ExitMonitor(mon); 152 } 153 154 static void PR_CALLBACK CXWriter(void* arg) { 155 CircBuf* cbp = (CircBuf*)arg; 156 PRInt32 i, n; 157 158 n = count / 2; 159 for (i = 0; i < n; i++) { 160 PutCBData(cbp, (void*)i); 161 } 162 163 PR_EnterMonitor(mon); 164 --alive; 165 PR_Notify(mon); 166 PR_ExitMonitor(mon); 167 } 168 169 static void CondWaitContextSwitch(PRThreadScope scope1, PRThreadScope scope2) { 170 PRThread *t1, *t2; 171 CircBuf* cbp; 172 173 PR_EnterMonitor(mon); 174 175 alive = 2; 176 177 cbp = NewCB(); 178 179 t1 = PR_CreateThread(PR_USER_THREAD, CXReader, cbp, PR_PRIORITY_NORMAL, 180 scope1, PR_UNJOINABLE_THREAD, 0); 181 PR_ASSERT(t1); 182 t2 = PR_CreateThread(PR_USER_THREAD, CXWriter, cbp, PR_PRIORITY_NORMAL, 183 scope2, PR_UNJOINABLE_THREAD, 0); 184 PR_ASSERT(t2); 185 186 /* Wait for both of the threads to exit */ 187 while (alive) { 188 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); 189 } 190 191 DeleteCB(cbp); 192 193 PR_ExitMonitor(mon); 194 } 195 196 static void CondWaitContextSwitchUU(void) { 197 CondWaitContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD); 198 } 199 200 static void CondWaitContextSwitchUK(void) { 201 CondWaitContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); 202 } 203 204 static void CondWaitContextSwitchKK(void) { 205 CondWaitContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); 206 } 207 208 /************************************************************************/ 209 210 static void Measure(void (*func)(void), const char* msg) { 211 PRIntervalTime start, stop; 212 double d; 213 214 start = PR_IntervalNow(); 215 (*func)(); 216 stop = PR_IntervalNow(); 217 218 d = (double)PR_IntervalToMicroseconds(stop - start); 219 220 if (debug_mode) { 221 printf("%40s: %6.2f usec\n", msg, d / count); 222 } 223 224 if (0 == d) { 225 failed = PR_TRUE; 226 } 227 } 228 229 static PRIntn PR_CALLBACK RealMain(int argc, char** argv) { 230 /* The command line argument: -d is used to determine if the test is being run 231 in debug mode. The regress tool requires only one line output:PASS or FAIL. 232 All of the printfs associated with this test has been handled with a if 233 (debug_mode) test. Usage: test_name [-d] [-c n] 234 */ 235 PLOptStatus os; 236 PLOptState* opt = PL_CreateOptState(argc, argv, "dc:"); 237 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { 238 if (PL_OPT_BAD == os) { 239 continue; 240 } 241 switch (opt->option) { 242 case 'd': /* debug mode */ 243 debug_mode = 1; 244 break; 245 case 'c': /* loop count */ 246 count = atoi(opt->value); 247 break; 248 default: 249 break; 250 } 251 } 252 PL_DestroyOptState(opt); 253 254 if (0 == count) { 255 count = DEFAULT_COUNT; 256 } 257 258 mon = PR_NewMonitor(); 259 260 Measure(CondWaitContextSwitchUU, "cond var wait context switch- user/user"); 261 Measure(CondWaitContextSwitchUK, "cond var wait context switch- user/kernel"); 262 Measure(CondWaitContextSwitchKK, 263 "cond var wait context switch- kernel/kernel"); 264 265 PR_DestroyMonitor(mon); 266 267 if (debug_mode) { 268 printf("%s\n", (failed) ? "FAILED" : "PASSED"); 269 } 270 271 if (failed) { 272 return 1; 273 } else { 274 return 0; 275 } 276 } 277 278 int main(int argc, char* argv[]) { 279 PRIntn rv; 280 281 rv = PR_Initialize(RealMain, argc, argv, 0); 282 return rv; 283 } /* main */