Back to index

lightning-sunbird  0.9+nobinonly
server_test.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /***********************************************************************
00039 **
00040 ** This server simulates a server running in loopback mode.
00041 **
00042 ** The idea is that a single server is created.  The server initially creates
00043 ** a number of worker threads.  Then, with the server running, a number of 
00044 ** clients are created which start requesting service from the server.
00045 **
00046 **
00047 ** Modification History:
00048 ** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
00049 **              The debug mode will print all of the printfs associated with this test.
00050 **                    The regress mode will be the default mode. Since the regress tool limits
00051 **           the output to a one line status:PASS or FAIL,all of the printf statements
00052 **                    have been handled with an if (debug_mode) statement. 
00053 ***********************************************************************/
00054 
00055 /***********************************************************************
00056 ** Includes
00057 ***********************************************************************/
00058 /* Used to get the command line option */
00059 #include "plgetopt.h"
00060 
00061 #include "nspr.h"
00062 #include "pprthred.h"
00063 
00064 #include <string.h>
00065 
00066 #define PORT 15004
00067 #define THREAD_STACKSIZE 0
00068 
00069 #define PASS 0
00070 #define FAIL 1
00071 static int debug_mode = 0;
00072 
00073 static int _iterations = 1000;
00074 static int _clients = 1;
00075 static int _client_data = 250;
00076 static int _server_data = (8*1024);
00077 
00078 static PRThreadScope ServerScope, ClientScope;
00079 
00080 #define SERVER "Server"
00081 #define MAIN   "Main"
00082 
00083 #define SERVER_STATE_STARTUP 0
00084 #define SERVER_STATE_READY   1
00085 #define SERVER_STATE_DYING   2
00086 #define SERVER_STATE_DEAD    4
00087 int       ServerState;
00088 PRLock    *ServerStateCVLock;
00089 PRCondVar *ServerStateCV;
00090 
00091 #undef DEBUGPRINTS
00092 #ifdef DEBUGPRINTS
00093 #define DPRINTF printf
00094 #else
00095 #define DPRINTF
00096 #endif
00097 
00098 
00099 /***********************************************************************
00100 ** PRIVATE FUNCTION:    Test_Result
00101 ** DESCRIPTION: Used in conjunction with the regress tool, prints out the
00102 **                          status of the test case.
00103 ** INPUTS:      PASS/FAIL
00104 ** OUTPUTS:     None
00105 ** RETURN:      None
00106 ** SIDE EFFECTS:
00107 **      
00108 ** RESTRICTIONS:
00109 **      None
00110 ** MEMORY:      NA
00111 ** ALGORITHM:   Determine what the status is and print accordingly.
00112 **      
00113 ***********************************************************************/
00114 
00115 
00116 static void Test_Result (int result)
00117 {
00118        switch (result)
00119        {
00120               case PASS:
00121                      printf ("PASS\n");
00122                      break;
00123               case FAIL:
00124                      printf ("FAIL\n");
00125                      break;
00126               default:
00127                      break;
00128        }
00129 }
00130 
00131 static void do_work(void);
00132 
00133 /* --- Server state functions --------------------------------------------- */
00134 void
00135 SetServerState(char *waiter, PRInt32 state)
00136 {
00137     PR_Lock(ServerStateCVLock);
00138     ServerState = state;
00139     PR_NotifyCondVar(ServerStateCV);
00140 
00141        if (debug_mode) DPRINTF("\t%s changed state to %d\n", waiter, state);
00142 
00143     PR_Unlock(ServerStateCVLock);
00144 }
00145 
00146 int
00147 WaitServerState(char *waiter, PRInt32 state)
00148 {
00149     PRInt32 rv;
00150 
00151     PR_Lock(ServerStateCVLock);
00152 
00153     if (debug_mode) DPRINTF("\t%s waiting for state %d\n", waiter, state);
00154 
00155     while(!(ServerState & state))
00156         PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
00157     rv = ServerState;
00158 
00159     if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n", 
00160         waiter, state, ServerState);
00161     PR_Unlock(ServerStateCVLock);
00162 
00163     return rv;
00164 }
00165 
00166 /* --- Server Functions ------------------------------------------- */
00167 
00168 PRLock *workerThreadsLock;
00169 PRInt32 workerThreads;
00170 PRInt32 workerThreadsBusy;
00171 
00172 void
00173 WorkerThreadFunc(void *_listenSock)
00174 {
00175     PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
00176     PRInt32 bytesRead;
00177     PRInt32 bytesWritten;
00178     char *dataBuf;
00179     char *sendBuf;
00180 
00181     if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
00182             _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32);
00183     dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32);
00184     if (!dataBuf)
00185         if (debug_mode) printf("\tServer could not malloc space!?\n");
00186     sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
00187     if (!sendBuf)
00188         if (debug_mode) printf("\tServer could not malloc space!?\n");
00189 
00190     if (debug_mode) DPRINTF("\tServer worker thread running\n");
00191 
00192     while(1) {
00193         PRInt32 bytesToRead = _client_data;
00194         PRInt32 bytesToWrite = _server_data;
00195         PRFileDesc *newSock;
00196         PRNetAddr *rAddr;
00197         PRInt32 loops = 0;
00198 
00199         loops++;
00200 
00201         if (debug_mode) DPRINTF("\tServer thread going into accept\n");
00202 
00203         bytesRead = PR_AcceptRead(listenSock, 
00204                                   &newSock,
00205                                   &rAddr,
00206                                   dataBuf,
00207                                   bytesToRead,
00208                                   PR_INTERVAL_NO_TIMEOUT);
00209 
00210         if (bytesRead < 0) {
00211             if (debug_mode) printf("\tServer error in accept (%d)\n", bytesRead);
00212             continue;
00213         }
00214 
00215         if (debug_mode) DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
00216         
00217         PR_AtomicIncrement(&workerThreadsBusy);
00218         if (workerThreadsBusy == workerThreads) {
00219 
00220             PR_Lock(workerThreadsLock);
00221             if (workerThreadsBusy == workerThreads) {
00222                 PRThread *WorkerThread;
00223 
00224                 WorkerThread = PR_CreateThread(
00225                                   PR_SYSTEM_THREAD,
00226                                   WorkerThreadFunc,
00227                                   listenSock,
00228                                   PR_PRIORITY_NORMAL,
00229                                   ServerScope,
00230                                   PR_UNJOINABLE_THREAD,
00231                                   THREAD_STACKSIZE);
00232 
00233                 if (!WorkerThread) {
00234                     if (debug_mode) printf("Error creating client thread %d\n", workerThreads);
00235                 } else {
00236                     PR_AtomicIncrement(&workerThreads);
00237                     if (debug_mode) DPRINTF("\tServer creates worker (%d)\n", workerThreads);
00238                 }
00239             }
00240             PR_Unlock(workerThreadsLock);
00241         }
00242  
00243         bytesToRead -= bytesRead;
00244         while (bytesToRead) {
00245             bytesRead = PR_Recv(newSock, 
00246                                 dataBuf, 
00247                                 bytesToRead, 
00248                                 0, 
00249                                 PR_INTERVAL_NO_TIMEOUT);
00250             if (bytesRead < 0) {
00251                 if (debug_mode) printf("\tServer error receiving data (%d)\n", bytesRead);
00252                 continue;
00253             }
00254             if (debug_mode) DPRINTF("\tServer received %d bytes\n", bytesRead);
00255         }
00256 
00257         bytesWritten = PR_Send(newSock,
00258                                sendBuf, 
00259                                bytesToWrite, 
00260                                0, 
00261                                PR_INTERVAL_NO_TIMEOUT);
00262         if (bytesWritten != _server_data) {
00263             if (debug_mode) printf("\tError sending data to client (%d, %d)\n", 
00264                 bytesWritten, PR_GetOSError());
00265         } else {
00266             if (debug_mode) DPRINTF("\tServer sent %d bytes\n", bytesWritten);
00267         }     
00268 
00269         PR_Close(newSock);
00270         PR_AtomicDecrement(&workerThreadsBusy);
00271     }
00272 }
00273 
00274 PRFileDesc *
00275 ServerSetup(void)
00276 {
00277     PRFileDesc *listenSocket;
00278     PRNetAddr serverAddr;
00279     PRThread *WorkerThread;
00280 
00281     if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
00282         if (debug_mode) printf("\tServer error creating listen socket\n");
00283               else Test_Result(FAIL);
00284         return NULL;
00285     }
00286 
00287     memset(&serverAddr, 0, sizeof(PRNetAddr));
00288     serverAddr.inet.family = PR_AF_INET;
00289     serverAddr.inet.port = PR_htons(PORT);
00290     serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
00291 
00292     if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
00293         if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
00294                 PR_GetOSError());
00295               else Test_Result(FAIL);
00296         PR_Close(listenSocket);
00297         return NULL;
00298     }
00299 
00300     if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
00301         if (debug_mode) printf("\tServer error listening to server socket\n");
00302               else Test_Result(FAIL);
00303         PR_Close(listenSocket);
00304 
00305         return NULL;
00306     }
00307 
00308     /* Create Clients */
00309     workerThreads = 0;
00310     workerThreadsBusy = 0;
00311 
00312     workerThreadsLock = PR_NewLock();
00313 
00314     WorkerThread = PR_CreateThread(
00315                       PR_SYSTEM_THREAD,
00316                       WorkerThreadFunc,
00317                       listenSocket,
00318                       PR_PRIORITY_NORMAL,
00319                       ServerScope,
00320                       PR_UNJOINABLE_THREAD,
00321                       THREAD_STACKSIZE);
00322 
00323     if (!WorkerThread) {
00324         if (debug_mode) printf("error creating working thread\n");
00325         PR_Close(listenSocket);
00326         return NULL;
00327     }
00328     PR_AtomicIncrement(&workerThreads);
00329     if (debug_mode) DPRINTF("\tServer created primordial worker thread\n");
00330 
00331     return listenSocket;
00332 }
00333 
00334 /* The main server loop */
00335 void
00336 ServerThreadFunc(void *unused)
00337 {
00338     PRFileDesc *listenSocket;
00339 
00340     /* Do setup */
00341     listenSocket = ServerSetup();
00342 
00343     if (!listenSocket) {
00344         SetServerState(SERVER, SERVER_STATE_DEAD);
00345     } else {
00346 
00347         if (debug_mode) DPRINTF("\tServer up\n");
00348 
00349         /* Tell clients they can start now. */
00350         SetServerState(SERVER, SERVER_STATE_READY);
00351 
00352         /* Now wait for server death signal */
00353         WaitServerState(SERVER, SERVER_STATE_DYING);
00354 
00355         /* Cleanup */
00356         SetServerState(SERVER, SERVER_STATE_DEAD);
00357     }
00358 }
00359 
00360 /* --- Client Functions ------------------------------------------- */
00361 
00362 PRInt32 numRequests;
00363 PRInt32 numClients;
00364 PRMonitor *clientMonitor;
00365 
00366 void
00367 ClientThreadFunc(void *unused)
00368 {
00369     PRNetAddr serverAddr;
00370     PRFileDesc *clientSocket;
00371     char *sendBuf;
00372     char *recvBuf;
00373     PRInt32 rv;
00374     PRInt32 bytesNeeded;
00375 
00376     sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
00377     if (!sendBuf)
00378         if (debug_mode) printf("\tClient could not malloc space!?\n");
00379     recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
00380     if (!recvBuf)
00381         if (debug_mode) printf("\tClient could not malloc space!?\n");
00382 
00383     memset(&serverAddr, 0, sizeof(PRNetAddr));
00384     serverAddr.inet.family = PR_AF_INET;
00385     serverAddr.inet.port = PR_htons(PORT);
00386     serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
00387 
00388     while(numRequests > 0) {
00389 
00390         if ( (numRequests % 10) == 0 )
00391             if (debug_mode) printf(".");
00392         if (debug_mode) DPRINTF("\tClient starting request %d\n", numRequests);
00393 
00394         clientSocket = PR_NewTCPSocket();
00395         if (!clientSocket) {
00396             if (debug_mode) printf("Client error creating socket: OS error %d\n",
00397                   PR_GetOSError());
00398             continue;
00399         }
00400 
00401         if (debug_mode) DPRINTF("\tClient connecting\n");
00402 
00403         rv = PR_Connect(clientSocket, 
00404                         &serverAddr,
00405                         PR_INTERVAL_NO_TIMEOUT);
00406         if (!clientSocket) {
00407             if (debug_mode) printf("\tClient error connecting\n");
00408             continue;
00409         }
00410 
00411         if (debug_mode) DPRINTF("\tClient connected\n");
00412 
00413         rv = PR_Send(clientSocket, 
00414                      sendBuf, 
00415                      _client_data, 
00416                      0, 
00417                      PR_INTERVAL_NO_TIMEOUT);
00418         if (rv != _client_data) {
00419             if (debug_mode) printf("Client error sending data (%d)\n", rv);
00420             PR_Close(clientSocket);
00421             continue;
00422         }
00423 
00424         if (debug_mode) DPRINTF("\tClient sent %d bytes\n", rv);
00425 
00426         bytesNeeded = _server_data;
00427         while(bytesNeeded) {
00428             rv = PR_Recv(clientSocket, 
00429                          recvBuf, 
00430                          bytesNeeded, 
00431                          0, 
00432                          PR_INTERVAL_NO_TIMEOUT);
00433             if (rv <= 0) {
00434                 if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n", 
00435                     rv, (_server_data - bytesNeeded), _server_data);
00436                 break;
00437             }
00438             if (debug_mode) DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
00439             bytesNeeded -= rv;
00440         }
00441 
00442         PR_Close(clientSocket);
00443  
00444         PR_AtomicDecrement(&numRequests);
00445     }
00446 
00447     PR_EnterMonitor(clientMonitor);
00448     --numClients;
00449     PR_Notify(clientMonitor);
00450     PR_ExitMonitor(clientMonitor);
00451 
00452     PR_DELETE(sendBuf);
00453     PR_DELETE(recvBuf);
00454 }
00455 
00456 void
00457 RunClients(void)
00458 {
00459     PRInt32 index;
00460 
00461     numRequests = _iterations;
00462     numClients = _clients;
00463     clientMonitor = PR_NewMonitor();
00464 
00465     for (index=0; index<_clients; index++) {
00466         PRThread *clientThread;
00467 
00468   
00469         clientThread = PR_CreateThread(
00470                           PR_USER_THREAD,
00471                           ClientThreadFunc,
00472                           NULL,
00473                           PR_PRIORITY_NORMAL,
00474                           ClientScope,
00475                           PR_UNJOINABLE_THREAD,
00476                           THREAD_STACKSIZE);
00477 
00478         if (!clientThread) {
00479             if (debug_mode) printf("\terror creating client thread %d\n", index);
00480         } else
00481             if (debug_mode) DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
00482 
00483     }
00484 
00485     PR_EnterMonitor(clientMonitor);
00486     while(numClients)
00487         PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
00488     PR_ExitMonitor(clientMonitor);
00489 }
00490 
00491 /* --- Main Function ---------------------------------------------- */
00492 
00493 static
00494 void do_work()
00495 {
00496     PRThread *ServerThread;
00497     PRInt32 state;
00498 
00499     SetServerState(MAIN, SERVER_STATE_STARTUP);
00500     ServerThread = PR_CreateThread(
00501                       PR_USER_THREAD,
00502                       ServerThreadFunc,
00503                       NULL,
00504                       PR_PRIORITY_NORMAL,
00505                       ServerScope,
00506                       PR_JOINABLE_THREAD,
00507                       THREAD_STACKSIZE);
00508     if (!ServerThread) {
00509         if (debug_mode) printf("error creating main server thread\n");
00510         return;
00511     }
00512 
00513     /* Wait for server to be ready */
00514     state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
00515 
00516     if (!(state & SERVER_STATE_DEAD)) {
00517         /* Run Test Clients */
00518         RunClients();
00519 
00520         /* Send death signal to server */
00521         SetServerState(MAIN, SERVER_STATE_DYING);
00522     }
00523 
00524     PR_JoinThread(ServerThread);
00525 }
00526 
00527 static void do_workUU(void)
00528 {
00529     ServerScope = PR_LOCAL_THREAD;
00530     ClientScope = PR_LOCAL_THREAD;
00531     do_work();
00532 }
00533 
00534 static void do_workUK(void)
00535 {
00536     ServerScope = PR_LOCAL_THREAD;
00537     ClientScope = PR_GLOBAL_THREAD;
00538     do_work();
00539 }
00540 
00541 static void do_workKU(void)
00542 {
00543     ServerScope = PR_GLOBAL_THREAD;
00544     ClientScope = PR_LOCAL_THREAD;
00545     do_work();
00546 }
00547 
00548 static void do_workKK(void)
00549 {
00550     ServerScope = PR_GLOBAL_THREAD;
00551     ClientScope = PR_GLOBAL_THREAD;
00552     do_work();
00553 }
00554 
00555 
00556 static void Measure(void (*func)(void), const char *msg)
00557 {
00558     PRIntervalTime start, stop;
00559     double d;
00560 
00561     start = PR_IntervalNow();
00562     (*func)();
00563     stop = PR_IntervalNow();
00564 
00565     d = (double)PR_IntervalToMicroseconds(stop - start);
00566 
00567     if (debug_mode) printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
00568 }
00569 
00570 
00571 main(int argc, char **argv)
00572 {
00573        /* The command line argument: -d is used to determine if the test is being run
00574        in debug mode. The regress tool requires only one line output:PASS or FAIL.
00575        All of the printfs associated with this test has been handled with a if (debug_mode)
00576        test.
00577        Usage: test_name -d
00578        */
00579        PLOptStatus os;
00580        PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
00581        while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
00582     {
00583               if (PL_OPT_BAD == os) continue;
00584         switch (opt->option)
00585         {
00586         case 'd':  /* debug mode */
00587                      debug_mode = 1;
00588             break;
00589          default:
00590             break;
00591         }
00592     }
00593        PL_DestroyOptState(opt);
00594 
00595  /* main test */
00596     if (debug_mode) {
00597               printf("Enter number of iterations: \n");
00598               scanf("%d", &_iterations);
00599               printf("Enter number of clients   : \n");
00600               scanf("%d", &_clients);
00601               printf("Enter size of client data : \n");
00602               scanf("%d", &_client_data);
00603               printf("Enter size of server data : \n");
00604               scanf("%d", &_server_data);
00605        }
00606        else {
00607               _iterations = 10;
00608            _clients = 1;
00609               _client_data = 10;
00610               _server_data = 10;
00611        }
00612        
00613     if (debug_mode) {
00614               printf("\n\n%d iterations with %d client threads.\n", 
00615         _iterations, _clients);
00616               printf("Sending %d bytes of client data and %d bytes of server data\n", 
00617         _client_data, _server_data);
00618        }
00619     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
00620     PR_STDIO_INIT();
00621 
00622     ServerStateCVLock = PR_NewLock();
00623     ServerStateCV = PR_NewCondVar(ServerStateCVLock);
00624 
00625     Measure(do_workUU, "server loop user/user");
00626  #if 0 
00627     Measure(do_workUK, "server loop user/kernel");
00628     Measure(do_workKU, "server loop kernel/user");
00629     Measure(do_workKK, "server loop kernel/kernel");
00630  #endif 
00631 
00632     PR_Cleanup();
00633 
00634     return 0;
00635 }