|
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 |
|
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 |
|
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 while (0 < gReading) |
|
74 PR_Wait (gMonitor, fiveSecs); |
|
75 gWriteWaiting--; |
|
76 } |
|
77 // -- write lock entered |
|
78 |
|
79 last = gCounter; |
|
80 gCounter++; |
|
81 |
|
82 spin (aWorkDelay); |
|
83 |
|
84 PR_ASSERT (gCounter == (last + 1)); // test invariance |
|
85 |
|
86 // -- exit write lock |
|
87 // if (0 < gReadWaiting) // notify waiting reads (do it anyway to show off the CondWait bug) |
|
88 PR_NotifyAll (gMonitor); |
|
89 |
|
90 PR_ExitMonitor (gMonitor); |
|
91 // -- write lock exited |
|
92 |
|
93 spin (aWaitDelay); |
|
94 } |
|
95 } |
|
96 |
|
97 void doReadThread (void* arg) |
|
98 { |
|
99 PRInt32 last; |
|
100 Arg_t *args = (Arg_t*)arg; |
|
101 PRInt32 aWorkDelay = args->a, aWaitDelay = args->b; |
|
102 PR_Sleep(0); |
|
103 |
|
104 while (1) |
|
105 { |
|
106 // -- enter read lock |
|
107 PR_EnterMonitor (gMonitor); |
|
108 |
|
109 if (0 < gWriteWaiting) // give up the monitor to waiting writes |
|
110 { |
|
111 PRIntervalTime fiveSecs = PR_SecondsToInterval(5); |
|
112 |
|
113 gReadWaiting++; |
|
114 if (gReadWaiting > gMaxReadWaits) // stats |
|
115 gMaxReadWaits = gReadWaiting; |
|
116 while (0 < gWriteWaiting) |
|
117 PR_Wait (gMonitor, fiveSecs); |
|
118 gReadWaiting--; |
|
119 } |
|
120 |
|
121 gReading++; |
|
122 |
|
123 gReads++; // stats |
|
124 if (gReading > gMaxReads) // stats |
|
125 gMaxReads = gReading; |
|
126 |
|
127 PR_ExitMonitor (gMonitor); |
|
128 // -- read lock entered |
|
129 |
|
130 last = gCounter; |
|
131 |
|
132 spin (aWorkDelay); |
|
133 |
|
134 PR_ASSERT (gCounter == last); // test invariance |
|
135 |
|
136 // -- exit read lock |
|
137 PR_EnterMonitor (gMonitor); // read unlock |
|
138 gReading--; |
|
139 |
|
140 // if ((0 == gReading) && (0 < gWriteWaiting)) // notify waiting writes (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 |
|
150 void fireThread ( |
|
151 char* aName, void (*aProc)(void *arg), Arg_t *aArg) |
|
152 { |
|
153 PRThread *thread = PR_CreateThread( |
|
154 PR_USER_THREAD, aProc, aArg, PR_PRIORITY_NORMAL, |
|
155 PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); |
|
156 } |
|
157 |
|
158 int pseudoMain (int argc, char** argv, char *pad) |
|
159 { |
|
160 PRInt32 lastWriteCount = gCounter; |
|
161 PRInt32 lastReadCount = gReads; |
|
162 Arg_t a1 = {500, 250}; |
|
163 Arg_t a2 = {500, 500}; |
|
164 Arg_t a3 = {250, 500}; |
|
165 Arg_t a4 = {750, 250}; |
|
166 Arg_t a5 = {100, 750}; |
|
167 Arg_t a6 = {100, 500}; |
|
168 Arg_t a7 = {100, 750}; |
|
169 |
|
170 gMonitor = PR_NewMonitor (); |
|
171 |
|
172 fireThread ("R1", doReadThread, &a1); |
|
173 fireThread ("R2", doReadThread, &a2); |
|
174 fireThread ("R3", doReadThread, &a3); |
|
175 fireThread ("R4", doReadThread, &a4); |
|
176 |
|
177 fireThread ("W1", doWriteThread, &a5); |
|
178 fireThread ("W2", doWriteThread, &a6); |
|
179 fireThread ("W3", doWriteThread, &a7); |
|
180 |
|
181 fireThread ("R5", doReadThread, &a1); |
|
182 fireThread ("R6", doReadThread, &a2); |
|
183 fireThread ("R7", doReadThread, &a3); |
|
184 fireThread ("R8", doReadThread, &a4); |
|
185 |
|
186 fireThread ("W4", doWriteThread, &a5); |
|
187 fireThread ("W5", doWriteThread, &a6); |
|
188 fireThread ("W6", doWriteThread, &a7); |
|
189 |
|
190 while (1) |
|
191 { |
|
192 PRInt32 writeCount, readCount; |
|
193 PRIntervalTime fiveSecs = PR_SecondsToInterval(5); |
|
194 PR_Sleep (fiveSecs); // get out of the way |
|
195 |
|
196 // print some stats, not threadsafe, informative only |
|
197 writeCount = gCounter; |
|
198 readCount = gReads; |
|
199 printf ("\ntick %d writes (+%d), %d reads (+%d) [max %d, %d, %d]", |
|
200 writeCount, writeCount - lastWriteCount, |
|
201 readCount, readCount - lastReadCount, |
|
202 gMaxReads, gMaxWriteWaits, gMaxReadWaits); |
|
203 lastWriteCount = writeCount; |
|
204 lastReadCount = readCount; |
|
205 gMaxReads = gMaxWriteWaits = gMaxReadWaits = 0; |
|
206 } |
|
207 return 0; |
|
208 } |
|
209 |
|
210 |
|
211 static void padStack (int argc, char** argv) |
|
212 { |
|
213 char pad[512]; /* Work around bug in nspr on windoze */ |
|
214 pseudoMain (argc, argv, pad); |
|
215 } |
|
216 |
|
217 int main(int argc, char **argv) |
|
218 { |
|
219 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); |
|
220 PR_STDIO_INIT(); |
|
221 padStack (argc, argv); |
|
222 } |
|
223 |
|
224 |
|
225 /* bug1test.c */ |