michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: Attached is a test program that uses the nspr1 to demonstrate a bug michael@0: under NT4.0. The fix has already been mentioned (add a ResetEvent just michael@0: before leaving the critical section in _PR_CondWait in hwmon.c). michael@0: */ michael@0: michael@0: #include "prthread.h" michael@0: #include "prtypes.h" michael@0: #include "prinit.h" michael@0: #include "prmon.h" michael@0: #include "prlog.h" michael@0: michael@0: typedef struct Arg_s michael@0: { michael@0: PRInt32 a, b; michael@0: } Arg_t; michael@0: michael@0: PRMonitor* gMonitor; // the monitor michael@0: PRInt32 gReading; // number of read locks michael@0: PRInt32 gWriteWaiting; // number of threads waiting for write lock michael@0: PRInt32 gReadWaiting; // number of threads waiting for read lock michael@0: michael@0: PRInt32 gCounter; // a counter michael@0: michael@0: // stats michael@0: PRInt32 gReads; // number of successful reads michael@0: PRInt32 gMaxReads; // max number of simultaneous reads michael@0: PRInt32 gMaxWriteWaits; // max number of writes that waited for read michael@0: PRInt32 gMaxReadWaits; // max number of reads that waited for write wait michael@0: michael@0: michael@0: void spin (PRInt32 aDelay) michael@0: { michael@0: PRInt32 index; michael@0: PRInt32 delay = aDelay * 1000; michael@0: michael@0: PR_Sleep(0); michael@0: michael@0: // randomize delay a bit michael@0: delay = (delay / 2) + (PRInt32)((float)delay * michael@0: ((float)rand () / (float)RAND_MAX)); michael@0: michael@0: for (index = 0; index < delay * 10; index++) michael@0: // consume a bunch of cpu cycles michael@0: ; michael@0: PR_Sleep(0); michael@0: } michael@0: michael@0: void doWriteThread (void* arg) michael@0: { michael@0: PRInt32 last; michael@0: Arg_t *args = (Arg_t*)arg; michael@0: PRInt32 aWorkDelay = args->a, aWaitDelay = args->b; michael@0: PR_Sleep(0); michael@0: michael@0: while (1) michael@0: { michael@0: // -- enter write lock michael@0: PR_EnterMonitor (gMonitor); michael@0: michael@0: if (0 < gReading) // wait for read locks to go away michael@0: { michael@0: PRIntervalTime fiveSecs = PR_SecondsToInterval(5); michael@0: michael@0: gWriteWaiting++; michael@0: if (gWriteWaiting > gMaxWriteWaits) // stats michael@0: gMaxWriteWaits = gWriteWaiting; michael@0: while (0 < gReading) michael@0: PR_Wait (gMonitor, fiveSecs); michael@0: gWriteWaiting--; michael@0: } michael@0: // -- write lock entered michael@0: michael@0: last = gCounter; michael@0: gCounter++; michael@0: michael@0: spin (aWorkDelay); michael@0: michael@0: PR_ASSERT (gCounter == (last + 1)); // test invariance michael@0: michael@0: // -- exit write lock michael@0: // if (0 < gReadWaiting) // notify waiting reads (do it anyway to show off the CondWait bug) michael@0: PR_NotifyAll (gMonitor); michael@0: michael@0: PR_ExitMonitor (gMonitor); michael@0: // -- write lock exited michael@0: michael@0: spin (aWaitDelay); michael@0: } michael@0: } michael@0: michael@0: void doReadThread (void* arg) michael@0: { michael@0: PRInt32 last; michael@0: Arg_t *args = (Arg_t*)arg; michael@0: PRInt32 aWorkDelay = args->a, aWaitDelay = args->b; michael@0: PR_Sleep(0); michael@0: michael@0: while (1) michael@0: { michael@0: // -- enter read lock michael@0: PR_EnterMonitor (gMonitor); michael@0: michael@0: if (0 < gWriteWaiting) // give up the monitor to waiting writes michael@0: { michael@0: PRIntervalTime fiveSecs = PR_SecondsToInterval(5); michael@0: michael@0: gReadWaiting++; michael@0: if (gReadWaiting > gMaxReadWaits) // stats michael@0: gMaxReadWaits = gReadWaiting; michael@0: while (0 < gWriteWaiting) michael@0: PR_Wait (gMonitor, fiveSecs); michael@0: gReadWaiting--; michael@0: } michael@0: michael@0: gReading++; michael@0: michael@0: gReads++; // stats michael@0: if (gReading > gMaxReads) // stats michael@0: gMaxReads = gReading; michael@0: michael@0: PR_ExitMonitor (gMonitor); michael@0: // -- read lock entered michael@0: michael@0: last = gCounter; michael@0: michael@0: spin (aWorkDelay); michael@0: michael@0: PR_ASSERT (gCounter == last); // test invariance michael@0: michael@0: // -- exit read lock michael@0: PR_EnterMonitor (gMonitor); // read unlock michael@0: gReading--; michael@0: michael@0: // if ((0 == gReading) && (0 < gWriteWaiting)) // notify waiting writes (do it anyway to show off the CondWait bug) michael@0: PR_NotifyAll (gMonitor); michael@0: PR_ExitMonitor (gMonitor); michael@0: // -- read lock exited michael@0: michael@0: spin (aWaitDelay); michael@0: } michael@0: } michael@0: michael@0: michael@0: void fireThread ( michael@0: char* aName, void (*aProc)(void *arg), Arg_t *aArg) michael@0: { michael@0: PRThread *thread = PR_CreateThread( michael@0: PR_USER_THREAD, aProc, aArg, PR_PRIORITY_NORMAL, michael@0: PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); michael@0: } michael@0: michael@0: int pseudoMain (int argc, char** argv, char *pad) michael@0: { michael@0: PRInt32 lastWriteCount = gCounter; michael@0: PRInt32 lastReadCount = gReads; michael@0: Arg_t a1 = {500, 250}; michael@0: Arg_t a2 = {500, 500}; michael@0: Arg_t a3 = {250, 500}; michael@0: Arg_t a4 = {750, 250}; michael@0: Arg_t a5 = {100, 750}; michael@0: Arg_t a6 = {100, 500}; michael@0: Arg_t a7 = {100, 750}; michael@0: michael@0: gMonitor = PR_NewMonitor (); michael@0: michael@0: fireThread ("R1", doReadThread, &a1); michael@0: fireThread ("R2", doReadThread, &a2); michael@0: fireThread ("R3", doReadThread, &a3); michael@0: fireThread ("R4", doReadThread, &a4); michael@0: michael@0: fireThread ("W1", doWriteThread, &a5); michael@0: fireThread ("W2", doWriteThread, &a6); michael@0: fireThread ("W3", doWriteThread, &a7); michael@0: michael@0: fireThread ("R5", doReadThread, &a1); michael@0: fireThread ("R6", doReadThread, &a2); michael@0: fireThread ("R7", doReadThread, &a3); michael@0: fireThread ("R8", doReadThread, &a4); michael@0: michael@0: fireThread ("W4", doWriteThread, &a5); michael@0: fireThread ("W5", doWriteThread, &a6); michael@0: fireThread ("W6", doWriteThread, &a7); michael@0: michael@0: while (1) michael@0: { michael@0: PRInt32 writeCount, readCount; michael@0: PRIntervalTime fiveSecs = PR_SecondsToInterval(5); michael@0: PR_Sleep (fiveSecs); // get out of the way michael@0: michael@0: // print some stats, not threadsafe, informative only michael@0: writeCount = gCounter; michael@0: readCount = gReads; michael@0: printf ("\ntick %d writes (+%d), %d reads (+%d) [max %d, %d, %d]", michael@0: writeCount, writeCount - lastWriteCount, michael@0: readCount, readCount - lastReadCount, michael@0: gMaxReads, gMaxWriteWaits, gMaxReadWaits); michael@0: lastWriteCount = writeCount; michael@0: lastReadCount = readCount; michael@0: gMaxReads = gMaxWriteWaits = gMaxReadWaits = 0; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: static void padStack (int argc, char** argv) michael@0: { michael@0: char pad[512]; /* Work around bug in nspr on windoze */ michael@0: pseudoMain (argc, argv, pad); michael@0: } michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); michael@0: PR_STDIO_INIT(); michael@0: padStack (argc, argv); michael@0: } michael@0: michael@0: michael@0: /* bug1test.c */