Wed, 31 Dec 2014 06:55:46 +0100
Added tag TORBROWSER_REPLICA for changeset 6474c204b198
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/. */
6 /*
7 ** File: ntioto.c
8 ** Description:
9 ** This test, ntioto.c, was designed to reproduce a bug reported by NES
10 ** on WindowsNT (fibers implementation). NSPR was asserting in ntio.c
11 ** after PR_AcceptRead() had timed out. I/O performed subsequent to the
12 ** call to PR_AcceptRead() could complete on a CPU other than the one
13 ** on which it was started. The assert in ntio.c detected this, then
14 ** asserted.
15 **
16 ** Design:
17 ** This test will fail with an assert in ntio.c if the problem it was
18 ** designed to catch occurs. It returns 0 otherwise.
19 **
20 ** The main() thread initializes and tears things down. A file is
21 ** opened for writing; this file will be written to by AcceptThread()
22 ** and JitterThread(). Main() creates a socket for reading, listens
23 ** and binds the socket.
24 **
25 ** ConnectThread() connects to the socket created by main, then polls
26 ** the "state" variable. When state is AllDone, ConnectThread() exits.
27 **
28 ** AcceptThread() calls PR_AcceptRead() on the socket. He fully expects
29 ** it to time out. After the timeout, AccpetThread() interacts with
30 ** JitterThread() via a common condition variable and the state
31 ** variable. The two threads ping-pong back and forth, each thread
32 ** writes the the file opened by main. This should provoke the
33 ** condition reported by NES (if we didn't fix it).
34 **
35 ** The failure is not solid. It may fail within a few ping-pongs between
36 ** AcceptThread() and JitterThread() or may take a while. The default
37 ** iteration count, jitter, is set by DEFAULT_JITTER. This may be
38 ** modified at the command line with the -j option.
39 **
40 */
42 #include <plgetopt.h>
43 #include <nspr.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
48 /*
49 ** Test harness infrastructure
50 */
51 PRLogModuleInfo *lm;
52 PRLogModuleLevel msgLevel = PR_LOG_NONE;
53 PRIntn debug = 0;
54 PRIntn verbose = 0;
55 PRUint32 failed_already = 0;
56 /* end Test harness infrastructure */
58 /* JITTER_DEFAULT: the number of times AcceptThread() and JitterThread() ping-pong */
59 #define JITTER_DEFAULT 100000
60 #define BASE_PORT 9867
62 PRIntervalTime timeout;
63 PRNetAddr listenAddr;
64 PRFileDesc *listenSock;
65 PRLock *ml;
66 PRCondVar *cv;
67 volatile enum {
68 RunJitter,
69 RunAcceptRead,
70 AllDone
71 } state = RunAcceptRead;
72 PRFileDesc *file1;
73 PRIntn iCounter = 0;
74 PRIntn jitter = JITTER_DEFAULT;
75 PRBool resume = PR_FALSE;
77 /*
78 ** Emit help text for this test
79 */
80 static void Help( void )
81 {
82 printf("Template: Help(): display your help message(s) here");
83 exit(1);
84 } /* end Help() */
87 /*
88 ** static computation of PR_AcceptRead() buffer size.
89 */
90 #define ACCEPT_READ_DATASIZE 10
91 #define ACCEPT_READ_BUFSIZE (PR_ACCEPT_READ_BUF_OVERHEAD + ACCEPT_READ_DATASIZE)
93 static void AcceptThread(void *arg)
94 {
95 PRIntn bytesRead;
96 char dataBuf[ACCEPT_READ_BUFSIZE];
97 PRFileDesc *arSock;
98 PRNetAddr *arAddr;
100 bytesRead = PR_AcceptRead( listenSock,
101 &arSock,
102 &arAddr,
103 dataBuf,
104 ACCEPT_READ_DATASIZE,
105 PR_SecondsToInterval(1));
107 if ( bytesRead == -1 && PR_GetError() == PR_IO_TIMEOUT_ERROR ) {
108 if ( debug ) printf("AcceptRead timed out\n");
109 } else {
110 if ( debug ) printf("Oops! read: %d, error: %d\n", bytesRead, PR_GetError());
111 }
113 while( state != AllDone ) {
114 PR_Lock( ml );
115 while( state != RunAcceptRead )
116 PR_WaitCondVar( cv, PR_INTERVAL_NO_TIMEOUT );
117 if ( ++iCounter >= jitter )
118 state = AllDone;
119 else
120 state = RunJitter;
121 if ( verbose ) printf(".");
122 PR_NotifyCondVar( cv );
123 PR_Unlock( ml );
124 PR_Write( file1, ".", 1 );
125 }
127 return;
128 } /* end AcceptThread() */
130 static void JitterThread(void *arg)
131 {
132 while( state != AllDone ) {
133 PR_Lock( ml );
134 while( state != RunJitter && state != AllDone )
135 PR_WaitCondVar( cv, PR_INTERVAL_NO_TIMEOUT );
136 if ( state != AllDone)
137 state = RunAcceptRead;
138 if ( verbose ) printf("+");
139 PR_NotifyCondVar( cv );
140 PR_Unlock( ml );
141 PR_Write( file1, "+", 1 );
142 }
143 return;
144 } /* end Goofy() */
146 static void ConnectThread( void *arg )
147 {
148 PRStatus rv;
149 PRFileDesc *clientSock;
150 PRNetAddr serverAddress;
151 clientSock = PR_NewTCPSocket();
153 PR_ASSERT(clientSock);
155 if ( resume ) {
156 if ( debug ) printf("pausing 3 seconds before connect\n");
157 PR_Sleep( PR_SecondsToInterval(3));
158 }
160 memset(&serverAddress, 0, sizeof(serverAddress));
161 rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &serverAddress);
162 PR_ASSERT( PR_SUCCESS == rv );
163 rv = PR_Connect( clientSock,
164 &serverAddress,
165 PR_SecondsToInterval(1));
166 PR_ASSERT( PR_SUCCESS == rv );
168 /* that's all we do. ... Wait for the acceptread() to timeout */
169 while( state != AllDone )
170 PR_Sleep( PR_SecondsToInterval(1));
171 return;
172 } /* end ConnectThread() */
175 int main(int argc, char **argv)
176 {
177 PRThread *tJitter;
178 PRThread *tAccept;
179 PRThread *tConnect;
180 PRStatus rv;
181 /* This test if valid for WinNT only! */
183 #if !defined(WINNT)
184 return 0;
185 #endif
187 {
188 /*
189 ** Get command line options
190 */
191 PLOptStatus os;
192 PLOptState *opt = PL_CreateOptState(argc, argv, "hdrvj:");
194 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
195 {
196 if (PL_OPT_BAD == os) continue;
197 switch (opt->option)
198 {
199 case 'd': /* debug */
200 debug = 1;
201 msgLevel = PR_LOG_ERROR;
202 break;
203 case 'v': /* verbose mode */
204 verbose = 1;
205 msgLevel = PR_LOG_DEBUG;
206 break;
207 case 'j':
208 jitter = atoi(opt->value);
209 if ( jitter == 0)
210 jitter = JITTER_DEFAULT;
211 break;
212 case 'r':
213 resume = PR_TRUE;
214 break;
215 case 'h': /* help message */
216 Help();
217 break;
218 default:
219 break;
220 }
221 }
222 PL_DestroyOptState(opt);
223 }
225 lm = PR_NewLogModule("Test"); /* Initialize logging */
227 /* set concurrency */
228 PR_SetConcurrency( 4 );
230 /* setup thread synchronization mechanics */
231 ml = PR_NewLock();
232 cv = PR_NewCondVar( ml );
234 /* setup a tcp socket */
235 memset(&listenAddr, 0, sizeof(listenAddr));
236 rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr);
237 PR_ASSERT( PR_SUCCESS == rv );
239 listenSock = PR_NewTCPSocket();
240 PR_ASSERT( listenSock );
242 rv = PR_Bind( listenSock, &listenAddr);
243 PR_ASSERT( PR_SUCCESS == rv );
245 rv = PR_Listen( listenSock, 5 );
246 PR_ASSERT( PR_SUCCESS == rv );
248 /* open a file for writing, provoke bug */
249 file1 = PR_Open("xxxTestFile", PR_CREATE_FILE | PR_RDWR, 666);
251 /* create Connect thread */
252 tConnect = PR_CreateThread(
253 PR_USER_THREAD, ConnectThread, NULL,
254 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
255 PR_JOINABLE_THREAD, 0 );
256 PR_ASSERT( tConnect );
258 /* create jitter off thread */
259 tJitter = PR_CreateThread(
260 PR_USER_THREAD, JitterThread, NULL,
261 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
262 PR_JOINABLE_THREAD, 0 );
263 PR_ASSERT( tJitter );
265 /* create acceptread thread */
266 tAccept = PR_CreateThread(
267 PR_USER_THREAD, AcceptThread, NULL,
268 PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
269 PR_JOINABLE_THREAD, 0 );
270 PR_ASSERT( tAccept );
272 /* wait for all threads to quit, then terminate gracefully */
273 PR_JoinThread( tConnect );
274 PR_JoinThread( tAccept );
275 PR_JoinThread( tJitter );
276 PR_Close( listenSock );
277 PR_DestroyCondVar(cv);
278 PR_DestroyLock(ml);
279 PR_Close( file1 );
280 PR_Delete( "xxxTestFile");
282 /* test return and exit */
283 if (debug) printf("%s\n", (failed_already)? "FAIL" : "PASS");
284 return( (failed_already == PR_TRUE )? 1 : 0 );
285 } /* main() */
286 /* end ntioto.c */