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