ntioto.c (7981B)
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 ** 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 */ 41 42 #include <plgetopt.h> 43 #include <nspr.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 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 */ 57 58 /* JITTER_DEFAULT: the number of times AcceptThread() and JitterThread() 59 * ping-pong */ 60 #define JITTER_DEFAULT 100000 61 62 #ifdef DEBUG 63 # define PORT_INC_DO +100 64 #else 65 # define PORT_INC_DO 66 #endif 67 #ifdef IS_64 68 # define PORT_INC_3264 +200 69 #else 70 # define PORT_INC_3264 71 #endif 72 73 #define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264 74 75 PRIntervalTime timeout; 76 PRNetAddr listenAddr; 77 PRFileDesc* listenSock; 78 PRLock* ml; 79 PRCondVar* cv; 80 volatile enum { RunJitter, RunAcceptRead, AllDone } state = RunAcceptRead; 81 PRFileDesc* file1; 82 PRIntn iCounter = 0; 83 PRIntn jitter = JITTER_DEFAULT; 84 PRBool resume = PR_FALSE; 85 86 /* 87 ** Emit help text for this test 88 */ 89 static void Help(void) { 90 printf("Template: Help(): display your help message(s) here"); 91 exit(1); 92 } /* end Help() */ 93 94 /* 95 ** static computation of PR_AcceptRead() buffer size. 96 */ 97 #define ACCEPT_READ_DATASIZE 10 98 #define ACCEPT_READ_BUFSIZE (PR_ACCEPT_READ_BUF_OVERHEAD + ACCEPT_READ_DATASIZE) 99 100 static void AcceptThread(void* arg) { 101 PRIntn bytesRead; 102 char dataBuf[ACCEPT_READ_BUFSIZE]; 103 PRFileDesc* arSock; 104 PRNetAddr* arAddr; 105 106 bytesRead = PR_AcceptRead(listenSock, &arSock, &arAddr, dataBuf, 107 ACCEPT_READ_DATASIZE, PR_SecondsToInterval(1)); 108 109 if (bytesRead == -1 && PR_GetError() == PR_IO_TIMEOUT_ERROR) { 110 if (debug) { 111 printf("AcceptRead timed out\n"); 112 } 113 } else { 114 if (debug) { 115 printf("Oops! read: %d, error: %d\n", bytesRead, PR_GetError()); 116 } 117 } 118 119 while (state != AllDone) { 120 PR_Lock(ml); 121 while (state != RunAcceptRead) { 122 PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT); 123 } 124 if (++iCounter >= jitter) { 125 state = AllDone; 126 } else { 127 state = RunJitter; 128 } 129 if (verbose) { 130 printf("."); 131 } 132 PR_NotifyCondVar(cv); 133 PR_Unlock(ml); 134 PR_Write(file1, ".", 1); 135 } 136 137 return; 138 } /* end AcceptThread() */ 139 140 static void JitterThread(void* arg) { 141 while (state != AllDone) { 142 PR_Lock(ml); 143 while (state != RunJitter && state != AllDone) { 144 PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT); 145 } 146 if (state != AllDone) { 147 state = RunAcceptRead; 148 } 149 if (verbose) { 150 printf("+"); 151 } 152 PR_NotifyCondVar(cv); 153 PR_Unlock(ml); 154 PR_Write(file1, "+", 1); 155 } 156 return; 157 } /* end Goofy() */ 158 159 static void ConnectThread(void* arg) { 160 PRStatus rv; 161 PRFileDesc* clientSock; 162 PRNetAddr serverAddress; 163 clientSock = PR_NewTCPSocket(); 164 165 PR_ASSERT(clientSock); 166 167 if (resume) { 168 if (debug) { 169 printf("pausing 3 seconds before connect\n"); 170 } 171 PR_Sleep(PR_SecondsToInterval(3)); 172 } 173 174 memset(&serverAddress, 0, sizeof(serverAddress)); 175 rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &serverAddress); 176 PR_ASSERT(PR_SUCCESS == rv); 177 rv = PR_Connect(clientSock, &serverAddress, PR_SecondsToInterval(1)); 178 PR_ASSERT(PR_SUCCESS == rv); 179 180 /* that's all we do. ... Wait for the acceptread() to timeout */ 181 while (state != AllDone) { 182 PR_Sleep(PR_SecondsToInterval(1)); 183 } 184 return; 185 } /* end ConnectThread() */ 186 187 int main(int argc, char** argv) { 188 PRThread* tJitter; 189 PRThread* tAccept; 190 PRThread* tConnect; 191 PRStatus rv; 192 /* This test if valid for WinNT only! */ 193 194 #if !defined(WINNT) 195 return 0; 196 #endif 197 198 { 199 /* 200 ** Get command line options 201 */ 202 PLOptStatus os; 203 PLOptState* opt = PL_CreateOptState(argc, argv, "hdrvj:"); 204 205 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { 206 if (PL_OPT_BAD == os) { 207 continue; 208 } 209 switch (opt->option) { 210 case 'd': /* debug */ 211 debug = 1; 212 msgLevel = PR_LOG_ERROR; 213 break; 214 case 'v': /* verbose mode */ 215 verbose = 1; 216 msgLevel = PR_LOG_DEBUG; 217 break; 218 case 'j': 219 jitter = atoi(opt->value); 220 if (jitter == 0) { 221 jitter = JITTER_DEFAULT; 222 } 223 break; 224 case 'r': 225 resume = PR_TRUE; 226 break; 227 case 'h': /* help message */ 228 Help(); 229 break; 230 default: 231 break; 232 } 233 } 234 PL_DestroyOptState(opt); 235 } 236 237 lm = PR_NewLogModule("Test"); /* Initialize logging */ 238 239 /* set concurrency */ 240 PR_SetConcurrency(4); 241 242 /* setup thread synchronization mechanics */ 243 ml = PR_NewLock(); 244 cv = PR_NewCondVar(ml); 245 246 /* setup a tcp socket */ 247 memset(&listenAddr, 0, sizeof(listenAddr)); 248 rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr); 249 PR_ASSERT(PR_SUCCESS == rv); 250 251 listenSock = PR_NewTCPSocket(); 252 PR_ASSERT(listenSock); 253 254 rv = PR_Bind(listenSock, &listenAddr); 255 PR_ASSERT(PR_SUCCESS == rv); 256 257 rv = PR_Listen(listenSock, 5); 258 PR_ASSERT(PR_SUCCESS == rv); 259 260 /* open a file for writing, provoke bug */ 261 file1 = PR_Open("xxxTestFile", PR_CREATE_FILE | PR_RDWR, 666); 262 263 /* create Connect thread */ 264 tConnect = 265 PR_CreateThread(PR_USER_THREAD, ConnectThread, NULL, PR_PRIORITY_NORMAL, 266 PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); 267 PR_ASSERT(tConnect); 268 269 /* create jitter off thread */ 270 tJitter = 271 PR_CreateThread(PR_USER_THREAD, JitterThread, NULL, PR_PRIORITY_NORMAL, 272 PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); 273 PR_ASSERT(tJitter); 274 275 /* create acceptread thread */ 276 tAccept = 277 PR_CreateThread(PR_USER_THREAD, AcceptThread, NULL, PR_PRIORITY_NORMAL, 278 PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); 279 PR_ASSERT(tAccept); 280 281 /* wait for all threads to quit, then terminate gracefully */ 282 PR_JoinThread(tConnect); 283 PR_JoinThread(tAccept); 284 PR_JoinThread(tJitter); 285 PR_Close(listenSock); 286 PR_DestroyCondVar(cv); 287 PR_DestroyLock(ml); 288 PR_Close(file1); 289 PR_Delete("xxxTestFile"); 290 291 /* test return and exit */ 292 if (debug) { 293 printf("%s\n", (failed_already) ? "FAIL" : "PASS"); 294 } 295 return ((failed_already == PR_TRUE) ? 1 : 0); 296 } /* main() */ 297 /* end ntioto.c */