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 ** 1996 - Netscape Communications Corporation
8 **
9 ** Name: cvar.c
10 **
11 ** Description: Tests Condition Variable Operations
12 **
13 ** Modification History:
14 ** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
15 ** The debug mode will print all of the printfs associated with this test.
16 ** The regress mode will be the default mode. Since the regress tool limits
17 ** the output to a one line status:PASS or FAIL,all of the printf statements
18 ** have been handled with an if (debug_mode) statement.
19 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
20 ** recognize the return code from tha main program.
21 ** 12-June-97 Revert to return code 0 and 1.
22 ***********************************************************************/
23
24 /***********************************************************************
25 ** Includes
26 ***********************************************************************/
27
28 #include "nspr.h"
29
30 /* Used to get the command line option */
31 #include "plgetopt.h"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 PRMonitor *mon;
38 #define DEFAULT_COUNT 1000
39 PRInt32 count = 0;
40 PRIntn debug_mode;
41
42 #define kQSIZE 1
43
44 typedef struct {
45 PRLock *bufLock;
46 int startIdx;
47 int numFull;
48 PRCondVar *notFull;
49 PRCondVar *notEmpty;
50 void *data[kQSIZE];
51 } CircBuf;
52
53 static PRBool failed = PR_FALSE;
54
55 /*
56 ** NewCB creates and initializes a new circular buffer.
57 */
NewCB(void)58 static CircBuf* NewCB(void)
59 {
60 CircBuf *cbp;
61
62 cbp = PR_NEW(CircBuf);
63 if (cbp == NULL)
64 return (NULL);
65
66 cbp->bufLock = PR_NewLock();
67 cbp->startIdx = 0;
68 cbp->numFull = 0;
69 cbp->notFull = PR_NewCondVar(cbp->bufLock);
70 cbp->notEmpty = PR_NewCondVar(cbp->bufLock);
71
72 return (cbp);
73 }
74
75 /*
76 ** DeleteCB frees a circular buffer.
77 */
DeleteCB(CircBuf * cbp)78 static void DeleteCB(CircBuf *cbp)
79 {
80 PR_DestroyLock(cbp->bufLock);
81 PR_DestroyCondVar(cbp->notFull);
82 PR_DestroyCondVar(cbp->notEmpty);
83 PR_DELETE(cbp);
84 }
85
86
87 /*
88 ** PutCBData puts new data on the queue. If the queue is full, it waits
89 ** until there is room.
90 */
PutCBData(CircBuf * cbp,void * data)91 static void PutCBData(CircBuf *cbp, void *data)
92 {
93 PR_Lock(cbp->bufLock);
94 /* wait while the buffer is full */
95 while (cbp->numFull == kQSIZE)
96 PR_WaitCondVar(cbp->notFull,PR_INTERVAL_NO_TIMEOUT);
97 cbp->data[(cbp->startIdx + cbp->numFull) % kQSIZE] = data;
98 cbp->numFull += 1;
99
100 /* let a waiting reader know that there is data */
101 PR_NotifyCondVar(cbp->notEmpty);
102 PR_Unlock(cbp->bufLock);
103
104 }
105
106
107 /*
108 ** GetCBData gets the oldest data on the queue. If the queue is empty, it waits
109 ** until new data appears.
110 */
GetCBData(CircBuf * cbp)111 static void* GetCBData(CircBuf *cbp)
112 {
113 void *data;
114
115 PR_Lock(cbp->bufLock);
116 /* wait while the buffer is empty */
117 while (cbp->numFull == 0)
118 PR_WaitCondVar(cbp->notEmpty,PR_INTERVAL_NO_TIMEOUT);
119 data = cbp->data[cbp->startIdx];
120 cbp->startIdx =(cbp->startIdx + 1) % kQSIZE;
121 cbp->numFull -= 1;
122
123 /* let a waiting writer know that there is room */
124 PR_NotifyCondVar(cbp->notFull);
125 PR_Unlock(cbp->bufLock);
126
127 return (data);
128 }
129
130
131 /************************************************************************/
132
133 static int alive;
134
CXReader(void * arg)135 static void PR_CALLBACK CXReader(void *arg)
136 {
137 CircBuf *cbp = (CircBuf *)arg;
138 PRInt32 i, n;
139 void *data;
140
141 n = count / 2;
142 for (i = 0; i < n; i++) {
143 data = GetCBData(cbp);
144 if ((int)data != i)
145 if (debug_mode) printf("data mismatch at for i = %d usec\n", i);
146 }
147
148 PR_EnterMonitor(mon);
149 --alive;
150 PR_Notify(mon);
151 PR_ExitMonitor(mon);
152 }
153
CXWriter(void * arg)154 static void PR_CALLBACK CXWriter(void *arg)
155 {
156 CircBuf *cbp = (CircBuf *)arg;
157 PRInt32 i, n;
158
159 n = count / 2;
160 for (i = 0; i < n; i++)
161 PutCBData(cbp, (void *)i);
162
163 PR_EnterMonitor(mon);
164 --alive;
165 PR_Notify(mon);
166 PR_ExitMonitor(mon);
167 }
168
CondWaitContextSwitch(PRThreadScope scope1,PRThreadScope scope2)169 static void CondWaitContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
170 {
171 PRThread *t1, *t2;
172 CircBuf *cbp;
173
174 PR_EnterMonitor(mon);
175
176 alive = 2;
177
178 cbp = NewCB();
179
180 t1 = PR_CreateThread(PR_USER_THREAD,
181 CXReader, cbp,
182 PR_PRIORITY_NORMAL,
183 scope1,
184 PR_UNJOINABLE_THREAD,
185 0);
186 PR_ASSERT(t1);
187 t2 = PR_CreateThread(PR_USER_THREAD,
188 CXWriter, cbp,
189 PR_PRIORITY_NORMAL,
190 scope2,
191 PR_UNJOINABLE_THREAD,
192 0);
193 PR_ASSERT(t2);
194
195 /* Wait for both of the threads to exit */
196 while (alive) {
197 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
198 }
199
200 DeleteCB(cbp);
201
202 PR_ExitMonitor(mon);
203 }
204
CondWaitContextSwitchUU(void)205 static void CondWaitContextSwitchUU(void)
206 {
207 CondWaitContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
208 }
209
CondWaitContextSwitchUK(void)210 static void CondWaitContextSwitchUK(void)
211 {
212 CondWaitContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
213 }
214
CondWaitContextSwitchKK(void)215 static void CondWaitContextSwitchKK(void)
216 {
217 CondWaitContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
218 }
219
220 /************************************************************************/
221
Measure(void (* func)(void),const char * msg)222 static void Measure(void (*func)(void), const char *msg)
223 {
224 PRIntervalTime start, stop;
225 double d;
226
227 start = PR_IntervalNow();
228 (*func)();
229 stop = PR_IntervalNow();
230
231 d = (double)PR_IntervalToMicroseconds(stop - start);
232
233 if (debug_mode) printf("%40s: %6.2f usec\n", msg, d / count);
234
235 if (0 == d) failed = PR_TRUE;
236 }
237
RealMain(int argc,char ** argv)238 static PRIntn PR_CALLBACK RealMain(int argc, char **argv)
239 {
240 /* The command line argument: -d is used to determine if the test is being run
241 in debug mode. The regress tool requires only one line output:PASS or FAIL.
242 All of the printfs associated with this test has been handled with a if (debug_mode)
243 test.
244 Usage: test_name [-d] [-c n]
245 */
246 PLOptStatus os;
247 PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
248 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
249 {
250 if (PL_OPT_BAD == os) continue;
251 switch (opt->option)
252 {
253 case 'd': /* debug mode */
254 debug_mode = 1;
255 break;
256 case 'c': /* loop count */
257 count = atoi(opt->value);
258 break;
259 default:
260 break;
261 }
262 }
263 PL_DestroyOptState(opt);
264
265 if (0 == count) count = DEFAULT_COUNT;
266
267 mon = PR_NewMonitor();
268
269 Measure(CondWaitContextSwitchUU, "cond var wait context switch- user/user");
270 Measure(CondWaitContextSwitchUK, "cond var wait context switch- user/kernel");
271 Measure(CondWaitContextSwitchKK, "cond var wait context switch- kernel/kernel");
272
273 PR_DestroyMonitor(mon);
274
275 if (debug_mode) printf("%s\n", (failed) ? "FAILED" : "PASSED");
276
277 if(failed)
278 return 1;
279 else
280 return 0;
281 }
282
283
main(int argc,char * argv[])284 int main(int argc, char *argv[])
285 {
286 PRIntn rv;
287
288 PR_STDIO_INIT();
289 rv = PR_Initialize(RealMain, argc, argv, 0);
290 return rv;
291 } /* main */
292