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