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