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