michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /*********************************************************************** michael@0: ** michael@0: ** This server simulates a server running in loopback mode. michael@0: ** michael@0: ** The idea is that a single server is created. The server initially creates michael@0: ** a number of worker threads. Then, with the server running, a number of michael@0: ** clients are created which start requesting service from the server. michael@0: ** michael@0: ** michael@0: ** Modification History: michael@0: ** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. michael@0: ** The debug mode will print all of the printfs associated with this test. michael@0: ** The regress mode will be the default mode. Since the regress tool limits michael@0: ** the output to a one line status:PASS or FAIL,all of the printf statements michael@0: ** have been handled with an if (debug_mode) statement. michael@0: ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to michael@0: ** recognize the return code from tha main program. michael@0: ***********************************************************************/ michael@0: michael@0: /*********************************************************************** michael@0: ** Includes michael@0: ***********************************************************************/ michael@0: /* Used to get the command line option */ michael@0: #include "plgetopt.h" michael@0: michael@0: #include "nspr.h" michael@0: #include "pprthred.h" michael@0: michael@0: #include michael@0: michael@0: #define PORT 15004 michael@0: #define THREAD_STACKSIZE 0 michael@0: michael@0: static int _iterations = 1000; michael@0: static int _clients = 1; michael@0: static int _client_data = 250; michael@0: static int _server_data = (8*1024); michael@0: michael@0: static PRThreadScope ServerScope, ClientScope; michael@0: michael@0: #define SERVER "Server" michael@0: #define MAIN "Main" michael@0: michael@0: #define SERVER_STATE_STARTUP 0 michael@0: #define SERVER_STATE_READY 1 michael@0: #define SERVER_STATE_DYING 2 michael@0: #define SERVER_STATE_DEAD 4 michael@0: int ServerState; michael@0: PRLock *ServerStateCVLock; michael@0: PRCondVar *ServerStateCV; michael@0: michael@0: #ifdef DEBUGPRINTS michael@0: #define DPRINTF printf michael@0: #else michael@0: #define DPRINTF michael@0: #endif michael@0: michael@0: PRIntn failed_already=0; michael@0: PRIntn debug_mode; michael@0: static void do_work(void); michael@0: michael@0: /* --- Server state functions --------------------------------------------- */ michael@0: void michael@0: SetServerState(char *waiter, PRInt32 state) michael@0: { michael@0: PR_Lock(ServerStateCVLock); michael@0: ServerState = state; michael@0: PR_NotifyCondVar(ServerStateCV); michael@0: michael@0: if (debug_mode) DPRINTF("\t%s changed state to %d\n", waiter, state); michael@0: michael@0: PR_Unlock(ServerStateCVLock); michael@0: } michael@0: michael@0: int michael@0: WaitServerState(char *waiter, PRInt32 state) michael@0: { michael@0: PRInt32 rv; michael@0: michael@0: PR_Lock(ServerStateCVLock); michael@0: michael@0: if (debug_mode) DPRINTF("\t%s waiting for state %d\n", waiter, state); michael@0: michael@0: while(!(ServerState & state)) michael@0: PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT); michael@0: rv = ServerState; michael@0: michael@0: if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n", michael@0: waiter, state, ServerState); michael@0: PR_Unlock(ServerStateCVLock); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* --- Server Functions ------------------------------------------- */ michael@0: michael@0: PRLock *workerThreadsLock; michael@0: PRInt32 workerThreads; michael@0: PRInt32 workerThreadsBusy; michael@0: michael@0: void michael@0: WorkerThreadFunc(void *_listenSock) michael@0: { michael@0: PRFileDesc *listenSock = (PRFileDesc *)_listenSock; michael@0: PRInt32 bytesRead; michael@0: PRInt32 bytesWritten; michael@0: char *dataBuf; michael@0: char *sendBuf; michael@0: michael@0: if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n", michael@0: _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32); michael@0: dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32); michael@0: if (!dataBuf) michael@0: if (debug_mode) printf("\tServer could not malloc space!?\n"); michael@0: sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char)); michael@0: if (!sendBuf) michael@0: if (debug_mode) printf("\tServer could not malloc space!?\n"); michael@0: michael@0: if (debug_mode) DPRINTF("\tServer worker thread running\n"); michael@0: michael@0: while(1) { michael@0: PRInt32 bytesToRead = _client_data; michael@0: PRInt32 bytesToWrite = _server_data; michael@0: PRFileDesc *newSock; michael@0: PRNetAddr *rAddr; michael@0: PRInt32 loops = 0; michael@0: michael@0: loops++; michael@0: michael@0: if (debug_mode) DPRINTF("\tServer thread going into accept\n"); michael@0: michael@0: bytesRead = PR_AcceptRead(listenSock, michael@0: &newSock, michael@0: &rAddr, michael@0: dataBuf, michael@0: bytesToRead, michael@0: PR_INTERVAL_NO_TIMEOUT); michael@0: michael@0: if (bytesRead < 0) { michael@0: if (debug_mode) printf("\tServer error in accept (%d)\n", bytesRead); michael@0: continue; michael@0: } michael@0: michael@0: if (debug_mode) DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead); michael@0: michael@0: PR_AtomicIncrement(&workerThreadsBusy); michael@0: #ifdef SYMBIAN michael@0: if (workerThreadsBusy == workerThreads && workerThreads<1) { michael@0: #else michael@0: if (workerThreadsBusy == workerThreads) { michael@0: #endif michael@0: PR_Lock(workerThreadsLock); michael@0: if (workerThreadsBusy == workerThreads) { michael@0: PRThread *WorkerThread; michael@0: michael@0: WorkerThread = PR_CreateThread( michael@0: PR_SYSTEM_THREAD, michael@0: WorkerThreadFunc, michael@0: listenSock, michael@0: PR_PRIORITY_NORMAL, michael@0: ServerScope, michael@0: PR_UNJOINABLE_THREAD, michael@0: THREAD_STACKSIZE); michael@0: michael@0: if (!WorkerThread) { michael@0: if (debug_mode) printf("Error creating client thread %d\n", workerThreads); michael@0: } else { michael@0: PR_AtomicIncrement(&workerThreads); michael@0: if (debug_mode) DPRINTF("\tServer creates worker (%d)\n", workerThreads); michael@0: } michael@0: } michael@0: PR_Unlock(workerThreadsLock); michael@0: } michael@0: michael@0: bytesToRead -= bytesRead; michael@0: while (bytesToRead) { michael@0: bytesRead = PR_Recv(newSock, michael@0: dataBuf, michael@0: bytesToRead, michael@0: 0, michael@0: PR_INTERVAL_NO_TIMEOUT); michael@0: if (bytesRead < 0) { michael@0: if (debug_mode) printf("\tServer error receiving data (%d)\n", bytesRead); michael@0: continue; michael@0: } michael@0: if (debug_mode) DPRINTF("\tServer received %d bytes\n", bytesRead); michael@0: } michael@0: michael@0: bytesWritten = PR_Send(newSock, michael@0: sendBuf, michael@0: bytesToWrite, michael@0: 0, michael@0: PR_INTERVAL_NO_TIMEOUT); michael@0: if (bytesWritten != _server_data) { michael@0: if (debug_mode) printf("\tError sending data to client (%d, %d)\n", michael@0: bytesWritten, PR_GetOSError()); michael@0: } else { michael@0: if (debug_mode) DPRINTF("\tServer sent %d bytes\n", bytesWritten); michael@0: } michael@0: michael@0: PR_Close(newSock); michael@0: PR_AtomicDecrement(&workerThreadsBusy); michael@0: } michael@0: } michael@0: michael@0: PRFileDesc * michael@0: ServerSetup(void) michael@0: { michael@0: PRFileDesc *listenSocket; michael@0: PRSocketOptionData sockOpt; michael@0: PRNetAddr serverAddr; michael@0: PRThread *WorkerThread; michael@0: michael@0: if ( (listenSocket = PR_NewTCPSocket()) == NULL) { michael@0: if (debug_mode) printf("\tServer error creating listen socket\n"); michael@0: else failed_already=1; michael@0: return NULL; michael@0: } michael@0: michael@0: sockOpt.option = PR_SockOpt_Reuseaddr; michael@0: sockOpt.value.reuse_addr = PR_TRUE; michael@0: if ( PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) { michael@0: if (debug_mode) printf("\tServer error setting socket option: OS error %d\n", michael@0: PR_GetOSError()); michael@0: else failed_already=1; michael@0: PR_Close(listenSocket); michael@0: return NULL; michael@0: } michael@0: michael@0: memset(&serverAddr, 0, sizeof(PRNetAddr)); michael@0: serverAddr.inet.family = PR_AF_INET; michael@0: serverAddr.inet.port = PR_htons(PORT); michael@0: serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY); michael@0: michael@0: if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) { michael@0: if (debug_mode) printf("\tServer error binding to server address: OS error %d\n", michael@0: PR_GetOSError()); michael@0: else failed_already=1; michael@0: PR_Close(listenSocket); michael@0: return NULL; michael@0: } michael@0: michael@0: if ( PR_Listen(listenSocket, 128) == PR_FAILURE) { michael@0: if (debug_mode) printf("\tServer error listening to server socket\n"); michael@0: else failed_already=1; michael@0: PR_Close(listenSocket); michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: /* Create Clients */ michael@0: workerThreads = 0; michael@0: workerThreadsBusy = 0; michael@0: michael@0: workerThreadsLock = PR_NewLock(); michael@0: michael@0: WorkerThread = PR_CreateThread( michael@0: PR_SYSTEM_THREAD, michael@0: WorkerThreadFunc, michael@0: listenSocket, michael@0: PR_PRIORITY_NORMAL, michael@0: ServerScope, michael@0: PR_UNJOINABLE_THREAD, michael@0: THREAD_STACKSIZE); michael@0: michael@0: if (!WorkerThread) { michael@0: if (debug_mode) printf("error creating working thread\n"); michael@0: PR_Close(listenSocket); michael@0: return NULL; michael@0: } michael@0: PR_AtomicIncrement(&workerThreads); michael@0: if (debug_mode) DPRINTF("\tServer created primordial worker thread\n"); michael@0: michael@0: return listenSocket; michael@0: } michael@0: michael@0: /* The main server loop */ michael@0: void michael@0: ServerThreadFunc(void *unused) michael@0: { michael@0: PRFileDesc *listenSocket; michael@0: michael@0: /* Do setup */ michael@0: listenSocket = ServerSetup(); michael@0: michael@0: if (!listenSocket) { michael@0: SetServerState(SERVER, SERVER_STATE_DEAD); michael@0: } else { michael@0: michael@0: if (debug_mode) DPRINTF("\tServer up\n"); michael@0: michael@0: /* Tell clients they can start now. */ michael@0: SetServerState(SERVER, SERVER_STATE_READY); michael@0: michael@0: /* Now wait for server death signal */ michael@0: WaitServerState(SERVER, SERVER_STATE_DYING); michael@0: michael@0: /* Cleanup */ michael@0: SetServerState(SERVER, SERVER_STATE_DEAD); michael@0: } michael@0: } michael@0: michael@0: /* --- Client Functions ------------------------------------------- */ michael@0: michael@0: PRInt32 numRequests; michael@0: PRInt32 numClients; michael@0: PRMonitor *clientMonitor; michael@0: michael@0: void michael@0: ClientThreadFunc(void *unused) michael@0: { michael@0: PRNetAddr serverAddr; michael@0: PRFileDesc *clientSocket; michael@0: char *sendBuf; michael@0: char *recvBuf; michael@0: PRInt32 rv; michael@0: PRInt32 bytesNeeded; michael@0: michael@0: sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char)); michael@0: if (!sendBuf) michael@0: if (debug_mode) printf("\tClient could not malloc space!?\n"); michael@0: recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char)); michael@0: if (!recvBuf) michael@0: if (debug_mode) printf("\tClient could not malloc space!?\n"); michael@0: michael@0: memset(&serverAddr, 0, sizeof(PRNetAddr)); michael@0: serverAddr.inet.family = PR_AF_INET; michael@0: serverAddr.inet.port = PR_htons(PORT); michael@0: serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); michael@0: michael@0: while(numRequests > 0) { michael@0: michael@0: if ( (numRequests % 10) == 0 ) michael@0: if (debug_mode) printf("."); michael@0: if (debug_mode) DPRINTF("\tClient starting request %d\n", numRequests); michael@0: michael@0: clientSocket = PR_NewTCPSocket(); michael@0: if (!clientSocket) { michael@0: if (debug_mode) printf("Client error creating socket: OS error %d\n", michael@0: PR_GetOSError()); michael@0: continue; michael@0: } michael@0: michael@0: if (debug_mode) DPRINTF("\tClient connecting\n"); michael@0: michael@0: rv = PR_Connect(clientSocket, michael@0: &serverAddr, michael@0: PR_INTERVAL_NO_TIMEOUT); michael@0: if (!clientSocket) { michael@0: if (debug_mode) printf("\tClient error connecting\n"); michael@0: continue; michael@0: } michael@0: michael@0: if (debug_mode) DPRINTF("\tClient connected\n"); michael@0: michael@0: rv = PR_Send(clientSocket, michael@0: sendBuf, michael@0: _client_data, michael@0: 0, michael@0: PR_INTERVAL_NO_TIMEOUT); michael@0: if (rv != _client_data) { michael@0: if (debug_mode) printf("Client error sending data (%d)\n", rv); michael@0: PR_Close(clientSocket); michael@0: continue; michael@0: } michael@0: michael@0: if (debug_mode) DPRINTF("\tClient sent %d bytes\n", rv); michael@0: michael@0: bytesNeeded = _server_data; michael@0: while(bytesNeeded) { michael@0: rv = PR_Recv(clientSocket, michael@0: recvBuf, michael@0: bytesNeeded, michael@0: 0, michael@0: PR_INTERVAL_NO_TIMEOUT); michael@0: if (rv <= 0) { michael@0: if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n", michael@0: rv, (_server_data - bytesNeeded), _server_data); michael@0: break; michael@0: } michael@0: if (debug_mode) DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv); michael@0: bytesNeeded -= rv; michael@0: } michael@0: michael@0: PR_Close(clientSocket); michael@0: michael@0: PR_AtomicDecrement(&numRequests); michael@0: } michael@0: michael@0: PR_EnterMonitor(clientMonitor); michael@0: --numClients; michael@0: PR_Notify(clientMonitor); michael@0: PR_ExitMonitor(clientMonitor); michael@0: michael@0: PR_DELETE(sendBuf); michael@0: PR_DELETE(recvBuf); michael@0: } michael@0: michael@0: void michael@0: RunClients(void) michael@0: { michael@0: PRInt32 index; michael@0: michael@0: numRequests = _iterations; michael@0: numClients = _clients; michael@0: clientMonitor = PR_NewMonitor(); michael@0: michael@0: for (index=0; index<_clients; index++) { michael@0: PRThread *clientThread; michael@0: michael@0: michael@0: clientThread = PR_CreateThread( michael@0: PR_USER_THREAD, michael@0: ClientThreadFunc, michael@0: NULL, michael@0: PR_PRIORITY_NORMAL, michael@0: ClientScope, michael@0: PR_UNJOINABLE_THREAD, michael@0: THREAD_STACKSIZE); michael@0: michael@0: if (!clientThread) { michael@0: if (debug_mode) printf("\terror creating client thread %d\n", index); michael@0: } else michael@0: if (debug_mode) DPRINTF("\tMain created client %d/%d\n", index+1, _clients); michael@0: michael@0: } michael@0: michael@0: PR_EnterMonitor(clientMonitor); michael@0: while(numClients) michael@0: PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT); michael@0: PR_ExitMonitor(clientMonitor); michael@0: } michael@0: michael@0: /* --- Main Function ---------------------------------------------- */ michael@0: michael@0: static michael@0: void do_work() michael@0: { michael@0: PRThread *ServerThread; michael@0: PRInt32 state; michael@0: michael@0: SetServerState(MAIN, SERVER_STATE_STARTUP); michael@0: ServerThread = PR_CreateThread( michael@0: PR_USER_THREAD, michael@0: ServerThreadFunc, michael@0: NULL, michael@0: PR_PRIORITY_NORMAL, michael@0: ServerScope, michael@0: PR_JOINABLE_THREAD, michael@0: THREAD_STACKSIZE); michael@0: if (!ServerThread) { michael@0: if (debug_mode) printf("error creating main server thread\n"); michael@0: return; michael@0: } michael@0: michael@0: /* Wait for server to be ready */ michael@0: state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD); michael@0: michael@0: if (!(state & SERVER_STATE_DEAD)) { michael@0: /* Run Test Clients */ michael@0: RunClients(); michael@0: michael@0: /* Send death signal to server */ michael@0: SetServerState(MAIN, SERVER_STATE_DYING); michael@0: } michael@0: michael@0: PR_JoinThread(ServerThread); michael@0: } michael@0: michael@0: static void do_workUU(void) michael@0: { michael@0: ServerScope = PR_LOCAL_THREAD; michael@0: ClientScope = PR_LOCAL_THREAD; michael@0: do_work(); michael@0: } michael@0: michael@0: static void do_workUK(void) michael@0: { michael@0: ServerScope = PR_LOCAL_THREAD; michael@0: ClientScope = PR_GLOBAL_THREAD; michael@0: do_work(); michael@0: } michael@0: michael@0: static void do_workKU(void) michael@0: { michael@0: ServerScope = PR_GLOBAL_THREAD; michael@0: ClientScope = PR_LOCAL_THREAD; michael@0: do_work(); michael@0: } michael@0: michael@0: static void do_workKK(void) michael@0: { michael@0: ServerScope = PR_GLOBAL_THREAD; michael@0: ClientScope = PR_GLOBAL_THREAD; michael@0: do_work(); michael@0: } michael@0: michael@0: michael@0: static void Measure(void (*func)(void), const char *msg) michael@0: { michael@0: PRIntervalTime start, stop; michael@0: double d; michael@0: michael@0: start = PR_IntervalNow(); michael@0: (*func)(); michael@0: stop = PR_IntervalNow(); michael@0: michael@0: d = (double)PR_IntervalToMicroseconds(stop - start); michael@0: michael@0: if (debug_mode) printf("\n%40s: %6.2f usec\n", msg, d / _iterations); michael@0: } michael@0: michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: /* The command line argument: -d is used to determine if the test is being run michael@0: in debug mode. The regress tool requires only one line output:PASS or FAIL. michael@0: All of the printfs associated with this test has been handled with a if (debug_mode) michael@0: test. michael@0: Usage: test_name -d michael@0: */ michael@0: PLOptStatus os; michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); michael@0: while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) michael@0: { michael@0: if (PL_OPT_BAD == os) continue; michael@0: switch (opt->option) michael@0: { michael@0: case 'd': /* debug mode */ michael@0: debug_mode = 1; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: michael@0: /* main test */ michael@0: #ifndef SYMBIAN michael@0: if (debug_mode) { michael@0: printf("Enter number of iterations: \n"); michael@0: scanf("%d", &_iterations); michael@0: printf("Enter number of clients : \n"); michael@0: scanf("%d", &_clients); michael@0: printf("Enter size of client data : \n"); michael@0: scanf("%d", &_client_data); michael@0: printf("Enter size of server data : \n"); michael@0: scanf("%d", &_server_data); michael@0: } michael@0: else michael@0: #endif michael@0: { michael@0: _iterations = 7; michael@0: _clients = 7; michael@0: _client_data = 100; michael@0: _server_data = 100; michael@0: } michael@0: michael@0: if (debug_mode) { michael@0: printf("\n\n%d iterations with %d client threads.\n", michael@0: _iterations, _clients); michael@0: printf("Sending %d bytes of client data and %d bytes of server data\n", michael@0: _client_data, _server_data); michael@0: } michael@0: PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); michael@0: PR_STDIO_INIT(); michael@0: michael@0: PR_SetThreadRecycleMode(64); michael@0: michael@0: ServerStateCVLock = PR_NewLock(); michael@0: ServerStateCV = PR_NewCondVar(ServerStateCVLock); michael@0: michael@0: michael@0: Measure(do_workKK, "server loop kernel/kernel"); michael@0: michael@0: PR_Cleanup(); michael@0: michael@0: if(failed_already) michael@0: return 1; michael@0: else michael@0: return 0; michael@0: michael@0: }