bug1test.c (5674B)
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 Attached is a test program that uses the nspr1 to demonstrate a bug 8 under NT4.0. The fix has already been mentioned (add a ResetEvent just 9 before leaving the critical section in _PR_CondWait in hwmon.c). 10 */ 11 12 #include "prthread.h" 13 #include "prtypes.h" 14 #include "prinit.h" 15 #include "prmon.h" 16 #include "prlog.h" 17 18 typedef struct Arg_s { 19 PRInt32 a, b; 20 } Arg_t; 21 22 PRMonitor* gMonitor; // the monitor 23 PRInt32 gReading; // number of read locks 24 PRInt32 gWriteWaiting; // number of threads waiting for write lock 25 PRInt32 gReadWaiting; // number of threads waiting for read lock 26 27 PRInt32 gCounter; // a counter 28 29 // stats 30 PRInt32 gReads; // number of successful reads 31 PRInt32 gMaxReads; // max number of simultaneous reads 32 PRInt32 gMaxWriteWaits; // max number of writes that waited for read 33 PRInt32 gMaxReadWaits; // max number of reads that waited for write wait 34 35 void spin(PRInt32 aDelay) { 36 PRInt32 index; 37 PRInt32 delay = aDelay * 1000; 38 39 PR_Sleep(0); 40 41 // randomize delay a bit 42 delay = 43 (delay / 2) + (PRInt32)((float)delay * ((float)rand() / (float)RAND_MAX)); 44 45 for (index = 0; index < delay * 10; index++) 46 // consume a bunch of cpu cycles 47 ; 48 PR_Sleep(0); 49 } 50 51 void doWriteThread(void* arg) { 52 PRInt32 last; 53 Arg_t* args = (Arg_t*)arg; 54 PRInt32 aWorkDelay = args->a, aWaitDelay = args->b; 55 PR_Sleep(0); 56 57 while (1) { 58 // -- enter write lock 59 PR_EnterMonitor(gMonitor); 60 61 if (0 < gReading) // wait for read locks to go away 62 { 63 PRIntervalTime fiveSecs = PR_SecondsToInterval(5); 64 65 gWriteWaiting++; 66 if (gWriteWaiting > gMaxWriteWaits) { // stats 67 gMaxWriteWaits = gWriteWaiting; 68 } 69 while (0 < gReading) { 70 PR_Wait(gMonitor, fiveSecs); 71 } 72 gWriteWaiting--; 73 } 74 // -- write lock entered 75 76 last = gCounter; 77 gCounter++; 78 79 spin(aWorkDelay); 80 81 PR_ASSERT(gCounter == (last + 1)); // test invariance 82 83 // -- exit write lock 84 // if (0 < gReadWaiting) // notify waiting reads (do it anyway to show 85 // off the CondWait bug) 86 PR_NotifyAll(gMonitor); 87 88 PR_ExitMonitor(gMonitor); 89 // -- write lock exited 90 91 spin(aWaitDelay); 92 } 93 } 94 95 void doReadThread(void* arg) { 96 PRInt32 last; 97 Arg_t* args = (Arg_t*)arg; 98 PRInt32 aWorkDelay = args->a, aWaitDelay = args->b; 99 PR_Sleep(0); 100 101 while (1) { 102 // -- enter read lock 103 PR_EnterMonitor(gMonitor); 104 105 if (0 < gWriteWaiting) // give up the monitor to waiting writes 106 { 107 PRIntervalTime fiveSecs = PR_SecondsToInterval(5); 108 109 gReadWaiting++; 110 if (gReadWaiting > gMaxReadWaits) { // stats 111 gMaxReadWaits = gReadWaiting; 112 } 113 while (0 < gWriteWaiting) { 114 PR_Wait(gMonitor, fiveSecs); 115 } 116 gReadWaiting--; 117 } 118 119 gReading++; 120 121 gReads++; // stats 122 if (gReading > gMaxReads) { // stats 123 gMaxReads = gReading; 124 } 125 126 PR_ExitMonitor(gMonitor); 127 // -- read lock entered 128 129 last = gCounter; 130 131 spin(aWorkDelay); 132 133 PR_ASSERT(gCounter == last); // test invariance 134 135 // -- exit read lock 136 PR_EnterMonitor(gMonitor); // read unlock 137 gReading--; 138 139 // if ((0 == gReading) && (0 < gWriteWaiting)) // notify waiting writes 140 // (do it anyway to show off the CondWait bug) 141 PR_NotifyAll(gMonitor); 142 PR_ExitMonitor(gMonitor); 143 // -- read lock exited 144 145 spin(aWaitDelay); 146 } 147 } 148 149 void fireThread(char* aName, void (*aProc)(void* arg), Arg_t* aArg) { 150 PRThread* thread = 151 PR_CreateThread(PR_USER_THREAD, aProc, aArg, PR_PRIORITY_NORMAL, 152 PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); 153 } 154 155 int pseudoMain(int argc, char** argv, char* pad) { 156 PRInt32 lastWriteCount = gCounter; 157 PRInt32 lastReadCount = gReads; 158 Arg_t a1 = {500, 250}; 159 Arg_t a2 = {500, 500}; 160 Arg_t a3 = {250, 500}; 161 Arg_t a4 = {750, 250}; 162 Arg_t a5 = {100, 750}; 163 Arg_t a6 = {100, 500}; 164 Arg_t a7 = {100, 750}; 165 166 gMonitor = PR_NewMonitor(); 167 168 fireThread("R1", doReadThread, &a1); 169 fireThread("R2", doReadThread, &a2); 170 fireThread("R3", doReadThread, &a3); 171 fireThread("R4", doReadThread, &a4); 172 173 fireThread("W1", doWriteThread, &a5); 174 fireThread("W2", doWriteThread, &a6); 175 fireThread("W3", doWriteThread, &a7); 176 177 fireThread("R5", doReadThread, &a1); 178 fireThread("R6", doReadThread, &a2); 179 fireThread("R7", doReadThread, &a3); 180 fireThread("R8", doReadThread, &a4); 181 182 fireThread("W4", doWriteThread, &a5); 183 fireThread("W5", doWriteThread, &a6); 184 fireThread("W6", doWriteThread, &a7); 185 186 while (1) { 187 PRInt32 writeCount, readCount; 188 PRIntervalTime fiveSecs = PR_SecondsToInterval(5); 189 PR_Sleep(fiveSecs); // get out of the way 190 191 // print some stats, not threadsafe, informative only 192 writeCount = gCounter; 193 readCount = gReads; 194 printf("\ntick %d writes (+%d), %d reads (+%d) [max %d, %d, %d]", 195 writeCount, writeCount - lastWriteCount, readCount, 196 readCount - lastReadCount, gMaxReads, gMaxWriteWaits, gMaxReadWaits); 197 lastWriteCount = writeCount; 198 lastReadCount = readCount; 199 gMaxReads = gMaxWriteWaits = gMaxReadWaits = 0; 200 } 201 return 0; 202 } 203 204 static void padStack(int argc, char** argv) { 205 char pad[512]; /* Work around bug in nspr on windoze */ 206 pseudoMain(argc, argv, pad); 207 } 208 209 int main(int argc, char** argv) { 210 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 211 padStack(argc, argv); 212 } 213 214 /* bug1test.c */