nsprpub/pr/tests/ntioto.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/nsprpub/pr/tests/ntioto.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,286 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/*
    1.10 +** File: ntioto.c
    1.11 +** Description: 
    1.12 +** This test, ntioto.c, was designed to reproduce a bug reported by NES
    1.13 +** on WindowsNT (fibers implementation). NSPR was asserting in ntio.c
    1.14 +** after PR_AcceptRead() had timed out. I/O performed subsequent to the
    1.15 +** call to PR_AcceptRead() could complete on a CPU other than the one
    1.16 +** on which it was started. The assert in ntio.c detected this, then
    1.17 +** asserted.
    1.18 +**
    1.19 +** Design:
    1.20 +** This test will fail with an assert in ntio.c if the problem it was
    1.21 +** designed to catch occurs. It returns 0 otherwise.
    1.22 +** 
    1.23 +** The main() thread initializes and tears things down. A file is
    1.24 +** opened for writing; this file will be written to by AcceptThread()
    1.25 +** and JitterThread().  Main() creates a socket for reading, listens
    1.26 +** and binds the socket.
    1.27 +** 
    1.28 +** ConnectThread() connects to the socket created by main, then polls
    1.29 +** the "state" variable. When state is AllDone, ConnectThread() exits.
    1.30 +**
    1.31 +** AcceptThread() calls PR_AcceptRead() on the socket. He fully expects
    1.32 +** it to time out. After the timeout, AccpetThread() interacts with
    1.33 +** JitterThread() via a common condition variable and the state
    1.34 +** variable. The two threads ping-pong back and forth, each thread
    1.35 +** writes the the file opened by main. This should provoke the
    1.36 +** condition reported by NES (if we didn't fix it).
    1.37 +**
    1.38 +** The failure is not solid. It may fail within a few ping-pongs between
    1.39 +** AcceptThread() and JitterThread() or may take a while. The default
    1.40 +** iteration count, jitter, is set by DEFAULT_JITTER. This may be
    1.41 +** modified at the command line with the -j option.
    1.42 +** 
    1.43 +*/
    1.44 +
    1.45 +#include <plgetopt.h> 
    1.46 +#include <nspr.h> 
    1.47 +#include <stdio.h>
    1.48 +#include <stdlib.h>
    1.49 +#include <string.h>
    1.50 +
    1.51 +/*
    1.52 +** Test harness infrastructure
    1.53 +*/
    1.54 +PRLogModuleInfo *lm;
    1.55 +PRLogModuleLevel msgLevel = PR_LOG_NONE;
    1.56 +PRIntn  debug = 0;
    1.57 +PRIntn  verbose = 0;
    1.58 +PRUint32  failed_already = 0;
    1.59 +/* end Test harness infrastructure */
    1.60 +
    1.61 +/* JITTER_DEFAULT: the number of times AcceptThread() and JitterThread() ping-pong */
    1.62 +#define JITTER_DEFAULT  100000
    1.63 +#define BASE_PORT 9867
    1.64 +
    1.65 +PRIntervalTime timeout;
    1.66 +PRNetAddr   listenAddr;
    1.67 +PRFileDesc  *listenSock;
    1.68 +PRLock      *ml;
    1.69 +PRCondVar   *cv;
    1.70 +volatile enum  {
    1.71 +    RunJitter,
    1.72 +    RunAcceptRead,
    1.73 +    AllDone
    1.74 +} state = RunAcceptRead;
    1.75 +PRFileDesc  *file1;
    1.76 +PRIntn  iCounter = 0;
    1.77 +PRIntn  jitter = JITTER_DEFAULT;
    1.78 +PRBool  resume = PR_FALSE;
    1.79 +
    1.80 +/*
    1.81 +** Emit help text for this test
    1.82 +*/
    1.83 +static void Help( void )
    1.84 +{
    1.85 +    printf("Template: Help(): display your help message(s) here");
    1.86 +    exit(1);
    1.87 +} /* end Help() */
    1.88 +
    1.89 +
    1.90 +/*
    1.91 +** static computation of PR_AcceptRead() buffer size.
    1.92 +*/
    1.93 +#define ACCEPT_READ_DATASIZE 10
    1.94 +#define ACCEPT_READ_BUFSIZE (PR_ACCEPT_READ_BUF_OVERHEAD + ACCEPT_READ_DATASIZE)
    1.95 +
    1.96 +static void AcceptThread(void *arg)
    1.97 +{
    1.98 +    PRIntn bytesRead;
    1.99 +    char dataBuf[ACCEPT_READ_BUFSIZE];
   1.100 +    PRFileDesc  *arSock;
   1.101 +    PRNetAddr   *arAddr;
   1.102 +
   1.103 +    bytesRead = PR_AcceptRead( listenSock, 
   1.104 +        &arSock,
   1.105 +        &arAddr,
   1.106 +        dataBuf,
   1.107 +        ACCEPT_READ_DATASIZE,
   1.108 +        PR_SecondsToInterval(1));
   1.109 +
   1.110 +    if ( bytesRead == -1 && PR_GetError() == PR_IO_TIMEOUT_ERROR ) {
   1.111 +        if ( debug ) printf("AcceptRead timed out\n");
   1.112 +    } else {
   1.113 +        if ( debug ) printf("Oops! read: %d, error: %d\n", bytesRead, PR_GetError());
   1.114 +    }
   1.115 +
   1.116 +    while( state != AllDone )  {
   1.117 +        PR_Lock( ml );
   1.118 +        while( state != RunAcceptRead )
   1.119 +            PR_WaitCondVar( cv, PR_INTERVAL_NO_TIMEOUT );
   1.120 +        if ( ++iCounter >= jitter )
   1.121 +            state = AllDone;
   1.122 +        else
   1.123 +            state = RunJitter;
   1.124 +        if ( verbose ) printf(".");
   1.125 +        PR_NotifyCondVar( cv );
   1.126 +        PR_Unlock( ml );
   1.127 +        PR_Write( file1, ".", 1 );
   1.128 +    }
   1.129 +
   1.130 +    return;
   1.131 +} /* end AcceptThread() */
   1.132 +
   1.133 +static void JitterThread(void *arg)
   1.134 +{
   1.135 +    while( state != AllDone )  {
   1.136 +        PR_Lock( ml );
   1.137 +        while( state != RunJitter && state != AllDone )
   1.138 +            PR_WaitCondVar( cv, PR_INTERVAL_NO_TIMEOUT );
   1.139 +        if ( state != AllDone)
   1.140 +            state = RunAcceptRead;
   1.141 +        if ( verbose ) printf("+");
   1.142 +        PR_NotifyCondVar( cv );
   1.143 +        PR_Unlock( ml );
   1.144 +        PR_Write( file1, "+", 1 );
   1.145 +    }
   1.146 +    return;
   1.147 +} /* end Goofy() */
   1.148 +
   1.149 +static void ConnectThread( void *arg )
   1.150 +{
   1.151 +    PRStatus    rv;
   1.152 +    PRFileDesc  *clientSock;
   1.153 +    PRNetAddr   serverAddress;
   1.154 +    clientSock = PR_NewTCPSocket();
   1.155 +
   1.156 +    PR_ASSERT(clientSock);
   1.157 +
   1.158 +    if ( resume ) {
   1.159 +        if ( debug ) printf("pausing 3 seconds before connect\n");
   1.160 +        PR_Sleep( PR_SecondsToInterval(3));
   1.161 +    }
   1.162 +
   1.163 +    memset(&serverAddress, 0, sizeof(serverAddress));
   1.164 +    rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &serverAddress);
   1.165 +    PR_ASSERT( PR_SUCCESS == rv );
   1.166 +    rv = PR_Connect( clientSock, 
   1.167 +        &serverAddress, 
   1.168 +        PR_SecondsToInterval(1));
   1.169 +    PR_ASSERT( PR_SUCCESS == rv );
   1.170 +
   1.171 +    /* that's all we do. ... Wait for the acceptread() to timeout */
   1.172 +    while( state != AllDone )
   1.173 +        PR_Sleep( PR_SecondsToInterval(1));
   1.174 +    return;
   1.175 +} /* end ConnectThread() */
   1.176 +
   1.177 +
   1.178 +int main(int argc, char **argv)
   1.179 +{
   1.180 +    PRThread *tJitter;
   1.181 +    PRThread *tAccept;
   1.182 +    PRThread *tConnect;
   1.183 +    PRStatus rv;
   1.184 +    /* This test if valid for WinNT only! */
   1.185 +
   1.186 +#if !defined(WINNT)
   1.187 +    return 0;
   1.188 +#endif
   1.189 +
   1.190 +    {
   1.191 +        /*
   1.192 +        ** Get command line options
   1.193 +        */
   1.194 +        PLOptStatus os;
   1.195 +        PLOptState *opt = PL_CreateOptState(argc, argv, "hdrvj:");
   1.196 +
   1.197 +	    while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
   1.198 +        {
   1.199 +		    if (PL_OPT_BAD == os) continue;
   1.200 +            switch (opt->option)
   1.201 +            {
   1.202 +            case 'd':  /* debug */
   1.203 +                debug = 1;
   1.204 +			    msgLevel = PR_LOG_ERROR;
   1.205 +                break;
   1.206 +            case 'v':  /* verbose mode */
   1.207 +                verbose = 1;
   1.208 +			    msgLevel = PR_LOG_DEBUG;
   1.209 +                break;
   1.210 +            case 'j':
   1.211 +                jitter = atoi(opt->value);
   1.212 +                if ( jitter == 0)
   1.213 +                    jitter = JITTER_DEFAULT;
   1.214 +                break;
   1.215 +            case 'r':
   1.216 +                resume = PR_TRUE;
   1.217 +                break;
   1.218 +            case 'h':  /* help message */
   1.219 +			    Help();
   1.220 +                break;
   1.221 +             default:
   1.222 +                break;
   1.223 +            }
   1.224 +        }
   1.225 +	    PL_DestroyOptState(opt);
   1.226 +    }
   1.227 +
   1.228 +    lm = PR_NewLogModule("Test");       /* Initialize logging */
   1.229 +
   1.230 +    /* set concurrency */
   1.231 +    PR_SetConcurrency( 4 );
   1.232 +
   1.233 +    /* setup thread synchronization mechanics */
   1.234 +    ml = PR_NewLock();
   1.235 +    cv = PR_NewCondVar( ml );
   1.236 +
   1.237 +    /* setup a tcp socket */
   1.238 +    memset(&listenAddr, 0, sizeof(listenAddr));
   1.239 +    rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr);
   1.240 +    PR_ASSERT( PR_SUCCESS == rv );
   1.241 +
   1.242 +    listenSock = PR_NewTCPSocket();
   1.243 +    PR_ASSERT( listenSock );
   1.244 +
   1.245 +    rv = PR_Bind( listenSock, &listenAddr);
   1.246 +    PR_ASSERT( PR_SUCCESS == rv );
   1.247 +
   1.248 +    rv = PR_Listen( listenSock, 5 );
   1.249 +    PR_ASSERT( PR_SUCCESS == rv );
   1.250 +
   1.251 +    /* open a file for writing, provoke bug */
   1.252 +    file1 = PR_Open("xxxTestFile", PR_CREATE_FILE | PR_RDWR, 666);
   1.253 +
   1.254 +    /* create Connect thread */
   1.255 +    tConnect = PR_CreateThread(
   1.256 +        PR_USER_THREAD, ConnectThread, NULL,
   1.257 +        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
   1.258 +        PR_JOINABLE_THREAD, 0 );
   1.259 +    PR_ASSERT( tConnect );
   1.260 +
   1.261 +    /* create jitter off thread */
   1.262 +    tJitter = PR_CreateThread(
   1.263 +        PR_USER_THREAD, JitterThread, NULL,
   1.264 +        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
   1.265 +        PR_JOINABLE_THREAD, 0 );
   1.266 +    PR_ASSERT( tJitter );
   1.267 +
   1.268 +    /* create acceptread thread */
   1.269 +    tAccept = PR_CreateThread(
   1.270 +        PR_USER_THREAD, AcceptThread, NULL,
   1.271 +        PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
   1.272 +        PR_JOINABLE_THREAD, 0 );
   1.273 +    PR_ASSERT( tAccept );
   1.274 +
   1.275 +    /* wait for all threads to quit, then terminate gracefully */
   1.276 +    PR_JoinThread( tConnect );
   1.277 +    PR_JoinThread( tAccept );
   1.278 +    PR_JoinThread( tJitter );
   1.279 +    PR_Close( listenSock );
   1.280 +    PR_DestroyCondVar(cv);
   1.281 +    PR_DestroyLock(ml);
   1.282 +    PR_Close( file1 );
   1.283 +    PR_Delete( "xxxTestFile");
   1.284 +
   1.285 +    /* test return and exit */
   1.286 +    if (debug) printf("%s\n", (failed_already)? "FAIL" : "PASS");
   1.287 +    return( (failed_already == PR_TRUE )? 1 : 0 );
   1.288 +}  /* main() */
   1.289 +/* end ntioto.c */

mercurial