cltsrv.c revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape Portable Runtime (NSPR).
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
*
* Notes:
* [1] lth. The call to Sleep() is a hack to get the test case to run
* on Windows 95. Without it, the test case fails with an error
* WSAECONNRESET following a recv() call. The error is caused by the
* server side thread termination without a shutdown() or closesocket()
* call. Windows docmunentation suggests that this is predicted
* behavior; that other platforms get away with it is ... serindipity.
* The test case should shutdown() or closesocket() before
* thread termination. I didn't have time to figure out where or how
* to do it. The Sleep() call inserts enough delay to allow the
* client side to recv() all his data before the server side thread
* terminates. Whew! ...
*
** Modification History:
* 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
* The debug mode will print all of the printfs associated with this test.
* The regress mode will be the default mode. Since the regress tool limits
* the output to a one line status:PASS or FAIL,all of the printf statements
* have been handled with an if (debug_mode) statement.
*/
#include "prclist.h"
#include "prcvar.h"
#include "prerror.h"
#include "prinit.h"
#include "prinrval.h"
#include "prio.h"
#include "prlock.h"
#include "prlog.h"
#include "prtime.h"
#include "prmem.h"
#include "prnetdb.h"
#include "prprf.h"
#include "prthread.h"
#include "pprio.h"
#include "primpl.h"
#include "plstr.h"
#include "plerror.h"
#include "plgetopt.h"
#include <stdlib.h>
#include <string.h>
#if defined(XP_UNIX)
#include <math.h>
#endif
#ifdef XP_MAC
#include "prlog.h"
#define printf PR_LogPrint
#endif
/*
** This is the beginning of the test
*/
#define RECV_FLAGS 0
#define SEND_FLAGS 0
#define DEFAULT_LOW 0
#define DEFAULT_HIGH 0
#define BUFFER_SIZE 1024
#define DEFAULT_BACKLOG 5
#define DEFAULT_PORT 12849
#define DEFAULT_CLIENTS 1
#define ALLOWED_IN_ACCEPT 1
#define DEFAULT_CLIPPING 1000
#define DEFAULT_WORKERS_MIN 1
#define DEFAULT_WORKERS_MAX 1
#define DEFAULT_SERVER "localhost"
#define DEFAULT_EXECUTION_TIME 10
#define DEFAULT_CLIENT_TIMEOUT 4000
#define DEFAULT_SERVER_TIMEOUT 4000
typedef struct CSWorker_s CSWorker_t;
typedef struct CSServer_s CSServer_t;
typedef enum Verbosity
{
} Verbosity;
struct CSWorker_s
{
};
struct CSPool_s
{
};
struct CSServer_s
{
struct /* controlling worker counts */
{
} workers;
/* statistics */
};
typedef struct CSDescriptor_s
{
typedef struct CSClient_s
{
/* statistics */
} CSClient_t;
#define TEST_LOG(l, p, a) \
do { \
} while (0)
#define TEST_ASSERT(_expr) \
{
#if DEBUG
#endif
} /* _MW_Assert */
{
}
{
char buffer[100];
} /* TimeOfDayMessage */
{
char buffer[1024];
{
if (PR_FAILURE == rv)
{
("\tClient(0x%p): conection failed (%d, %d)\n",
goto aborted;
}
if (sizeof(CSDescriptor_t) != bytes)
{
if (PR_IO_TIMEOUT_ERROR == PR_GetError())
{
("\tClient(0x%p): send descriptor timeout\n", me));
goto retry;
}
}
netbytes = 0;
{
{
if (PR_IO_TIMEOUT_ERROR == PR_GetError())
{
("\tClient(0x%p): send data timeout\n", me));
goto retry;
}
}
}
filebytes = 0;
{
if (-1 == bytes)
{
if (Aborted(PR_FAILURE))
{
("\tClient(0x%p): receive data aborted\n", me));
goto aborted;
}
else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
("\tClient(0x%p): receive data timeout\n", me));
else
("\tClient(0x%p): receive error (%d, %d)\n",
goto retry;
}
if (0 == bytes)
{
("\t\tClient(0x%p): unexpected end of stream\n",
PR_CurrentThread()));
break;
}
}
("\tClient(0x%p): disconnected from server\n", me));
}
("\tClient(0x%p): stopped after %u operations and %u bytes\n",
} /* Client */
{
char buffer[1024];
("\tProcessRequest(0x%p): receiving desciptor\n", me));
if (-1 == bytes)
{
rv = PR_FAILURE;
if (PR_IO_TIMEOUT_ERROR == PR_GetError())
{
("\tProcessRequest(0x%p): receive timeout\n", me));
}
goto exit;
}
if (0 == bytes)
{
rv = PR_FAILURE;
("\tProcessRequest(0x%p): unexpected end of file\n", me));
goto exit;
}
("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n",
{
rv = PR_FAILURE;
if (PR_IO_TIMEOUT_ERROR == PR_GetError())
{
("\tProcessRequest(0x%p): open file timeout\n", me));
goto aborted;
}
}
filebytes = 0;
{
if (-1 == bytes)
{
rv = PR_FAILURE;
if (PR_IO_TIMEOUT_ERROR == PR_GetError())
{
("\t\tProcessRequest(0x%p): receive data timeout\n", me));
goto aborted;
}
/*
* XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
* on NT here. This is equivalent to ECONNRESET on Unix.
* -wtc
*/
("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n",
goto aborted;
}
if(0 == bytes)
{
("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
rv = PR_FAILURE;
goto aborted;
}
/* The byte count for PR_Write should be positive */
{
rv = PR_FAILURE;
if (PR_IO_TIMEOUT_ERROR == PR_GetError())
{
("\t\tProcessRequest(0x%p): write file timeout\n", me));
goto aborted;
}
}
TEST_ASSERT(bytes > 0);
}
{
rv = PR_FAILURE;
if (PR_IO_TIMEOUT_ERROR == PR_GetError())
{
("\t\tProcessRequest(0x%p): open file timeout\n",
PR_CurrentThread()));
goto aborted;
}
("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n",
goto aborted;
}
netbytes = 0;
{
{
rv = PR_FAILURE;
if (PR_IO_TIMEOUT_ERROR == PR_GetError())
("\t\tProcessRequest(0x%p): read file timeout\n", me));
else
("\t\tProcessRequest(0x%p): other file error (%d, %d)\n",
goto aborted;
}
TEST_ASSERT(bytes > 0);
{
rv = PR_FAILURE;
if (PR_IO_TIMEOUT_ERROR == PR_GetError())
{
("\t\tProcessRequest(0x%p): send data timeout\n", me));
goto aborted;
}
break;
}
TEST_ASSERT(bytes > 0);
}
exit:
("\t\tProcessRequest(0x%p): Finished\n", me));
#if defined(WIN95)
#endif
return rv;
} /* ProcessRequest */
{
PR_UNJOINABLE_THREAD, 0);
{
return PR_FAILURE;
}
("\tCreateWorker(0x%p): create new worker (0x%p)\n",
return PR_SUCCESS;
} /* CreateWorker */
{
{
{
("\t\tWorker(0x%p): waiting for accept slot[%d]\n",
{
("\tWorker(0x%p): has been %s\n",
goto exit;
}
}
("\t\tWorker(0x%p): calling accept\n", me));
{
{
}
goto exit;
}
{
/*
** Create another worker of the total number of workers is
** less than the minimum specified or we have none left in
** accept() AND we're not over the maximum.
** This sort of presumes that the number allowed in accept
** is at least as many as the minimum. Otherwise we'll keep
** creating new threads and deleting them soon after.
*/
if (PR_SUCCESS != rv)
("\t\tWorker(0x%p): server process ended abnormally\n", me));
}
}
exit:
{
}
} /* Worker */
{
if (PR_AF_INET6 != domain)
else
/*
** Create the first worker (actually, a thread that accepts
** connections and then processes the work load as needed).
** From this point on, additional worker threads are created
** as they are needed by existing worker threads.
*/
/*
** From here on this thread is merely hanging around as the contact
** point for the main test driver. It's just waiting for the driver
** to declare the test complete.
*/
("\tServer(0x%p): waiting for state change\n", me));
{
}
("\tServer(0x%p): shutting down workers\n", me));
/*
** Get all the worker threads to exit. They know how to
** clean up after themselves, so this is just a matter of
** waiting for clorine in the pool to take effect. During
** this stage we're ignoring interrupts.
*/
{
}
{
("\tServer(0x%p): waiting for %u workers to exit\n",
}
("\tServer(0x%p): stopped after %u operations and %u bytes\n",
} /* Server */
{
while (execution > 0)
{
}
} /* WaitForCompletion */
static void Help(void)
{
} /* Help */
static Verbosity IncrementVerbosity(void)
{
} /* IncrementVerbosity */
{
const char *serverName = DEFAULT_SERVER;
/*
* -G use global threads
* -a <n> threads allowed in accept
* -b <n> backlock for listen
* -c <threads> number of clients to create
* -f <low> low water mark for caching FDs
* -F <high> high water mark for caching FDs
* -w <threads> minimal number of server threads
* -W <threads> maximum number of server threads
* -e <seconds> duration of the test in seconds
* -s <string> dsn name of server (implies no server here)
* -v verbosity
*/
{
if (PL_OPT_BAD == os) continue;
{
case 'G': /* use global threads */
break;
case 'X': /* use XTP as transport */
protocol = 36;
break;
case '6': /* Use IPv6 */
break;
case 'a': /* the value for accepting */
break;
case 'b': /* the value for backlock */
break;
case 'c': /* number of client threads */
break;
case 'f': /* low water fd cache */
break;
case 'F': /* low water fd cache */
break;
case 'w': /* minimum server worker threads */
break;
case 'W': /* maximum server worker threads */
break;
case 'e': /* program execution time in seconds */
break;
case 's': /* server's address */
break;
case 'v': /* verbosity */
break;
case 'd': /* debug mode */
break;
case 'p': /* pthread mode */
break;
case 'h':
default:
Help();
return 2;
}
}
#ifdef XP_MAC
#endif
if (serverIsLocal)
{
/* Establish the server */
("main(0x%p): starting server\n", PR_CurrentThread()));
("main(0x%p): creating server thread\n", PR_CurrentThread()));
("main(0x%p): waiting for server init\n", PR_CurrentThread()));
("main(0x%p): server init complete (port #%d)\n",
}
if (clients != 0)
{
/* Create all of the clients */
char buffer[BUFFER_SIZE];
("main(0x%p): creating %d client threads\n",
PR_CurrentThread(), clients));
if (!serverIsLocal)
{
if (PR_SUCCESS != rv)
{
return 2;
}
}
{
if (serverIsLocal)
{
if (PR_AF_INET6 != domain)
(void)PR_InitializeNetAddr(
else
}
else
{
(void)PR_EnumerateHostEnt(
}
("main(0x%p): creating client threads\n", PR_CurrentThread()));
}
}
/* Then just let them go at it for a bit */
("main(0x%p): waiting for execution interval (%d seconds)\n",
PR_CurrentThread(), execution));
if (clients != 0)
{
{
("main(0x%p): notifying client(0x%p) to stop\n",
{
}
("main(0x%p): joining client(0x%p)\n",
}
}
{
/* All clients joined - retrieve the server */
("main(0x%p): notifying server(0x%p) to stop\n",
("main(0x%p): joining server(0x%p)\n",
}
("main(0x%p): test complete\n", PR_CurrentThread()));
PR_Cleanup();
return 0;
} /* main */
/* cltsrv.c */