nsprpub/pr/tests/ntioto.c

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

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 */

mercurial