tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

server_test.c (16137B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /***********************************************************************
      7 **
      8 ** This server simulates a server running in loopback mode.
      9 **
     10 ** The idea is that a single server is created.  The server initially creates
     11 ** a number of worker threads.  Then, with the server running, a number of
     12 ** clients are created which start requesting service from the server.
     13 **
     14 **
     15 ** Modification History:
     16 ** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
     17 **           The debug mode will print all of the printfs associated with this
     18 *test.
     19 **           The regress mode will be the default mode. Since the regress tool
     20 *limits
     21 **           the output to a one line status:PASS or FAIL,all of the printf
     22 *statements
     23 **           have been handled with an if (debug_mode) statement.
     24 ***********************************************************************/
     25 
     26 /***********************************************************************
     27 ** Includes
     28 ***********************************************************************/
     29 /* Used to get the command line option */
     30 #include "plgetopt.h"
     31 
     32 #include "nspr.h"
     33 #include "pprthred.h"
     34 
     35 #include <string.h>
     36 
     37 #define PORT 15004
     38 #define THREAD_STACKSIZE 0
     39 
     40 #define PASS 0
     41 #define FAIL 1
     42 static int debug_mode = 0;
     43 static int failed_already = 0;
     44 
     45 static int _iterations = 1000;
     46 static int _clients = 1;
     47 static int _client_data = 250;
     48 static int _server_data = (8 * 1024);
     49 
     50 static PRThreadScope ServerScope, ClientScope;
     51 
     52 #define SERVER "Server"
     53 #define MAIN "Main"
     54 
     55 #define SERVER_STATE_STARTUP 0
     56 #define SERVER_STATE_READY 1
     57 #define SERVER_STATE_DYING 2
     58 #define SERVER_STATE_DEAD 4
     59 int ServerState;
     60 PRLock* ServerStateCVLock;
     61 PRCondVar* ServerStateCV;
     62 
     63 #undef DEBUGPRINTS
     64 #ifdef DEBUGPRINTS
     65 #  define DPRINTF printf
     66 #else
     67 #  define DPRINTF
     68 #endif
     69 
     70 /***********************************************************************
     71 ** PRIVATE FUNCTION:    Test_Result
     72 ** DESCRIPTION: Used in conjunction with the regress tool, prints out the
     73 **              status of the test case.
     74 ** INPUTS:      PASS/FAIL
     75 ** OUTPUTS:     None
     76 ** RETURN:      None
     77 ** SIDE EFFECTS:
     78 **
     79 ** RESTRICTIONS:
     80 **      None
     81 ** MEMORY:      NA
     82 ** ALGORITHM:   Determine what the status is and print accordingly.
     83 **
     84 ***********************************************************************/
     85 
     86 static void Test_Result(int result) {
     87  switch (result) {
     88    case PASS:
     89      printf("PASS\n");
     90      break;
     91    case FAIL:
     92      printf("FAIL\n");
     93      failed_already = 1;
     94      break;
     95    default:
     96      break;
     97  }
     98 }
     99 
    100 static void do_work(void);
    101 
    102 /* --- Server state functions --------------------------------------------- */
    103 void SetServerState(char* waiter, PRInt32 state) {
    104  PR_Lock(ServerStateCVLock);
    105  ServerState = state;
    106  PR_NotifyCondVar(ServerStateCV);
    107 
    108  if (debug_mode) {
    109    DPRINTF("\t%s changed state to %d\n", waiter, state);
    110  }
    111 
    112  PR_Unlock(ServerStateCVLock);
    113 }
    114 
    115 int WaitServerState(char* waiter, PRInt32 state) {
    116  PRInt32 rv;
    117 
    118  PR_Lock(ServerStateCVLock);
    119 
    120  if (debug_mode) {
    121    DPRINTF("\t%s waiting for state %d\n", waiter, state);
    122  }
    123 
    124  while (!(ServerState & state)) {
    125    PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
    126  }
    127  rv = ServerState;
    128 
    129  if (debug_mode)
    130    DPRINTF("\t%s resuming from wait for state %d; state now %d\n", waiter,
    131            state, ServerState);
    132  PR_Unlock(ServerStateCVLock);
    133 
    134  return rv;
    135 }
    136 
    137 /* --- Server Functions ------------------------------------------- */
    138 
    139 PRLock* workerThreadsLock;
    140 PRInt32 workerThreads;
    141 PRInt32 workerThreadsBusy;
    142 
    143 void WorkerThreadFunc(void* _listenSock) {
    144  PRFileDesc* listenSock = (PRFileDesc*)_listenSock;
    145  PRInt32 bytesRead;
    146  PRInt32 bytesWritten;
    147  char* dataBuf;
    148  char* sendBuf;
    149 
    150  if (debug_mode)
    151    DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
    152            _client_data + (2 * sizeof(PRNetAddr)) + 32, _client_data,
    153            (2 * sizeof(PRNetAddr)) + 32);
    154  dataBuf = (char*)PR_MALLOC(_client_data + 2 * sizeof(PRNetAddr) + 32);
    155  if (!dataBuf)
    156    if (debug_mode) {
    157      printf("\tServer could not malloc space!?\n");
    158    }
    159  sendBuf = (char*)PR_MALLOC(_server_data * sizeof(char));
    160  if (!sendBuf)
    161    if (debug_mode) {
    162      printf("\tServer could not malloc space!?\n");
    163    }
    164 
    165  if (debug_mode) {
    166    DPRINTF("\tServer worker thread running\n");
    167  }
    168 
    169  while (1) {
    170    PRInt32 bytesToRead = _client_data;
    171    PRInt32 bytesToWrite = _server_data;
    172    PRFileDesc* newSock;
    173    PRNetAddr* rAddr;
    174    PRInt32 loops = 0;
    175 
    176    loops++;
    177 
    178    if (debug_mode) {
    179      DPRINTF("\tServer thread going into accept\n");
    180    }
    181 
    182    bytesRead = PR_AcceptRead(listenSock, &newSock, &rAddr, dataBuf,
    183                              bytesToRead, PR_INTERVAL_NO_TIMEOUT);
    184 
    185    if (bytesRead < 0) {
    186      if (debug_mode) {
    187        printf("\tServer error in accept (%d)\n", bytesRead);
    188      }
    189      continue;
    190    }
    191 
    192    if (debug_mode) {
    193      DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
    194    }
    195 
    196    PR_AtomicIncrement(&workerThreadsBusy);
    197    if (workerThreadsBusy == workerThreads) {
    198      PR_Lock(workerThreadsLock);
    199      if (workerThreadsBusy == workerThreads) {
    200        PRThread* WorkerThread;
    201 
    202        WorkerThread = PR_CreateThread(
    203            PR_SYSTEM_THREAD, WorkerThreadFunc, listenSock, PR_PRIORITY_NORMAL,
    204            ServerScope, PR_UNJOINABLE_THREAD, THREAD_STACKSIZE);
    205 
    206        if (!WorkerThread) {
    207          if (debug_mode) {
    208            printf("Error creating client thread %d\n", workerThreads);
    209          }
    210        } else {
    211          PR_AtomicIncrement(&workerThreads);
    212          if (debug_mode) {
    213            DPRINTF("\tServer creates worker (%d)\n", workerThreads);
    214          }
    215        }
    216      }
    217      PR_Unlock(workerThreadsLock);
    218    }
    219 
    220    bytesToRead -= bytesRead;
    221    while (bytesToRead) {
    222      bytesRead =
    223          PR_Recv(newSock, dataBuf, bytesToRead, 0, PR_INTERVAL_NO_TIMEOUT);
    224      if (bytesRead < 0) {
    225        if (debug_mode) {
    226          printf("\tServer error receiving data (%d)\n", bytesRead);
    227        }
    228        continue;
    229      }
    230      if (debug_mode) {
    231        DPRINTF("\tServer received %d bytes\n", bytesRead);
    232      }
    233    }
    234 
    235    bytesWritten =
    236        PR_Send(newSock, sendBuf, bytesToWrite, 0, PR_INTERVAL_NO_TIMEOUT);
    237    if (bytesWritten != _server_data) {
    238      if (debug_mode)
    239        printf("\tError sending data to client (%d, %d)\n", bytesWritten,
    240               PR_GetOSError());
    241    } else {
    242      if (debug_mode) {
    243        DPRINTF("\tServer sent %d bytes\n", bytesWritten);
    244      }
    245    }
    246 
    247    PR_Close(newSock);
    248    PR_AtomicDecrement(&workerThreadsBusy);
    249  }
    250 }
    251 
    252 PRFileDesc* ServerSetup(void) {
    253  PRFileDesc* listenSocket;
    254  PRSocketOptionData sockOpt;
    255  PRNetAddr serverAddr;
    256  PRThread* WorkerThread;
    257 
    258  if ((listenSocket = PR_NewTCPSocket()) == NULL) {
    259    if (debug_mode) {
    260      printf("\tServer error creating listen socket\n");
    261    } else {
    262      Test_Result(FAIL);
    263    }
    264    return NULL;
    265  }
    266 
    267  sockOpt.option = PR_SockOpt_Reuseaddr;
    268  sockOpt.value.reuse_addr = PR_TRUE;
    269  if (PR_SetSocketOption(listenSocket, &sockOpt) != PR_SUCCESS) {
    270    if (debug_mode)
    271      printf("\tServer error setting socket option: OS error %d\n",
    272             PR_GetOSError());
    273    else {
    274      Test_Result(FAIL);
    275    }
    276    PR_Close(listenSocket);
    277    return NULL;
    278  }
    279 
    280  memset(&serverAddr, 0, sizeof(PRNetAddr));
    281  serverAddr.inet.family = PR_AF_INET;
    282  serverAddr.inet.port = PR_htons(PORT);
    283  serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
    284 
    285  if (PR_Bind(listenSocket, &serverAddr) != PR_SUCCESS) {
    286    if (debug_mode)
    287      printf("\tServer error binding to server address: OS error %d\n",
    288             PR_GetOSError());
    289    else {
    290      Test_Result(FAIL);
    291    }
    292    PR_Close(listenSocket);
    293    return NULL;
    294  }
    295 
    296  if (PR_Listen(listenSocket, 128) != PR_SUCCESS) {
    297    if (debug_mode) {
    298      printf("\tServer error listening to server socket\n");
    299    } else {
    300      Test_Result(FAIL);
    301    }
    302    PR_Close(listenSocket);
    303 
    304    return NULL;
    305  }
    306 
    307  /* Create Clients */
    308  workerThreads = 0;
    309  workerThreadsBusy = 0;
    310 
    311  workerThreadsLock = PR_NewLock();
    312 
    313  WorkerThread = PR_CreateThread(PR_SYSTEM_THREAD, WorkerThreadFunc,
    314                                 listenSocket, PR_PRIORITY_NORMAL, ServerScope,
    315                                 PR_UNJOINABLE_THREAD, THREAD_STACKSIZE);
    316 
    317  if (!WorkerThread) {
    318    if (debug_mode) {
    319      printf("error creating working thread\n");
    320    }
    321    PR_Close(listenSocket);
    322    return NULL;
    323  }
    324  PR_AtomicIncrement(&workerThreads);
    325  if (debug_mode) {
    326    DPRINTF("\tServer created primordial worker thread\n");
    327  }
    328 
    329  return listenSocket;
    330 }
    331 
    332 /* The main server loop */
    333 void ServerThreadFunc(void* unused) {
    334  PRFileDesc* listenSocket;
    335 
    336  /* Do setup */
    337  listenSocket = ServerSetup();
    338 
    339  if (!listenSocket) {
    340    SetServerState(SERVER, SERVER_STATE_DEAD);
    341  } else {
    342    if (debug_mode) {
    343      DPRINTF("\tServer up\n");
    344    }
    345 
    346    /* Tell clients they can start now. */
    347    SetServerState(SERVER, SERVER_STATE_READY);
    348 
    349    /* Now wait for server death signal */
    350    WaitServerState(SERVER, SERVER_STATE_DYING);
    351 
    352    /* Cleanup */
    353    SetServerState(SERVER, SERVER_STATE_DEAD);
    354  }
    355 }
    356 
    357 /* --- Client Functions ------------------------------------------- */
    358 
    359 PRInt32 numRequests;
    360 PRInt32 numClients;
    361 PRMonitor* clientMonitor;
    362 
    363 void ClientThreadFunc(void* unused) {
    364  PRNetAddr serverAddr;
    365  PRFileDesc* clientSocket;
    366  char* sendBuf;
    367  char* recvBuf;
    368  PRInt32 rv;
    369  PRInt32 bytesNeeded;
    370 
    371  sendBuf = (char*)PR_MALLOC(_client_data * sizeof(char));
    372  if (!sendBuf)
    373    if (debug_mode) {
    374      printf("\tClient could not malloc space!?\n");
    375    }
    376  recvBuf = (char*)PR_MALLOC(_server_data * sizeof(char));
    377  if (!recvBuf)
    378    if (debug_mode) {
    379      printf("\tClient could not malloc space!?\n");
    380    }
    381 
    382  memset(&serverAddr, 0, sizeof(PRNetAddr));
    383  serverAddr.inet.family = PR_AF_INET;
    384  serverAddr.inet.port = PR_htons(PORT);
    385  serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
    386 
    387  while (numRequests > 0) {
    388    if ((numRequests % 10) == 0)
    389      if (debug_mode) {
    390        printf(".");
    391      }
    392    if (debug_mode) {
    393      DPRINTF("\tClient starting request %d\n", numRequests);
    394    }
    395 
    396    clientSocket = PR_NewTCPSocket();
    397    if (!clientSocket) {
    398      if (debug_mode)
    399        printf("Client error creating socket: OS error %d\n", PR_GetOSError());
    400      continue;
    401    }
    402 
    403    if (debug_mode) {
    404      DPRINTF("\tClient connecting\n");
    405    }
    406 
    407    rv = PR_Connect(clientSocket, &serverAddr, PR_INTERVAL_NO_TIMEOUT);
    408    if (!clientSocket) {
    409      if (debug_mode) {
    410        printf("\tClient error connecting\n");
    411      }
    412      continue;
    413    }
    414 
    415    if (debug_mode) {
    416      DPRINTF("\tClient connected\n");
    417    }
    418 
    419    rv =
    420        PR_Send(clientSocket, sendBuf, _client_data, 0, PR_INTERVAL_NO_TIMEOUT);
    421    if (rv != _client_data) {
    422      if (debug_mode) {
    423        printf("Client error sending data (%d)\n", rv);
    424      }
    425      PR_Close(clientSocket);
    426      continue;
    427    }
    428 
    429    if (debug_mode) {
    430      DPRINTF("\tClient sent %d bytes\n", rv);
    431    }
    432 
    433    bytesNeeded = _server_data;
    434    while (bytesNeeded) {
    435      rv = PR_Recv(clientSocket, recvBuf, bytesNeeded, 0,
    436                   PR_INTERVAL_NO_TIMEOUT);
    437      if (rv <= 0) {
    438        if (debug_mode)
    439          printf("Client error receiving data (%d) (%d/%d)\n", rv,
    440                 (_server_data - bytesNeeded), _server_data);
    441        break;
    442      }
    443      if (debug_mode) {
    444        DPRINTF("\tClient received %d bytes; need %d more\n", rv,
    445                bytesNeeded - rv);
    446      }
    447      bytesNeeded -= rv;
    448    }
    449 
    450    PR_Close(clientSocket);
    451 
    452    PR_AtomicDecrement(&numRequests);
    453  }
    454 
    455  PR_EnterMonitor(clientMonitor);
    456  --numClients;
    457  PR_Notify(clientMonitor);
    458  PR_ExitMonitor(clientMonitor);
    459 
    460  PR_DELETE(sendBuf);
    461  PR_DELETE(recvBuf);
    462 }
    463 
    464 void RunClients(void) {
    465  PRInt32 index;
    466 
    467  numRequests = _iterations;
    468  numClients = _clients;
    469  clientMonitor = PR_NewMonitor();
    470 
    471  for (index = 0; index < _clients; index++) {
    472    PRThread* clientThread;
    473 
    474    clientThread = PR_CreateThread(PR_USER_THREAD, ClientThreadFunc, NULL,
    475                                   PR_PRIORITY_NORMAL, ClientScope,
    476                                   PR_UNJOINABLE_THREAD, THREAD_STACKSIZE);
    477 
    478    if (!clientThread) {
    479      if (debug_mode) {
    480        printf("\terror creating client thread %d\n", index);
    481      }
    482    } else if (debug_mode) {
    483      DPRINTF("\tMain created client %d/%d\n", index + 1, _clients);
    484    }
    485  }
    486 
    487  PR_EnterMonitor(clientMonitor);
    488  while (numClients) {
    489    PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
    490  }
    491  PR_ExitMonitor(clientMonitor);
    492 }
    493 
    494 /* --- Main Function ---------------------------------------------- */
    495 
    496 static void do_work() {
    497  PRThread* ServerThread;
    498  PRInt32 state;
    499 
    500  SetServerState(MAIN, SERVER_STATE_STARTUP);
    501  ServerThread = PR_CreateThread(PR_USER_THREAD, ServerThreadFunc, NULL,
    502                                 PR_PRIORITY_NORMAL, ServerScope,
    503                                 PR_JOINABLE_THREAD, THREAD_STACKSIZE);
    504  if (!ServerThread) {
    505    if (debug_mode) {
    506      printf("error creating main server thread\n");
    507    }
    508    return;
    509  }
    510 
    511  /* Wait for server to be ready */
    512  state = WaitServerState(MAIN, SERVER_STATE_READY | SERVER_STATE_DEAD);
    513 
    514  if (!(state & SERVER_STATE_DEAD)) {
    515    /* Run Test Clients */
    516    RunClients();
    517 
    518    /* Send death signal to server */
    519    SetServerState(MAIN, SERVER_STATE_DYING);
    520  }
    521 
    522  PR_JoinThread(ServerThread);
    523 }
    524 
    525 static void do_workUU(void) {
    526  ServerScope = PR_LOCAL_THREAD;
    527  ClientScope = PR_LOCAL_THREAD;
    528  do_work();
    529 }
    530 
    531 static void do_workUK(void) {
    532  ServerScope = PR_LOCAL_THREAD;
    533  ClientScope = PR_GLOBAL_THREAD;
    534  do_work();
    535 }
    536 
    537 static void do_workKU(void) {
    538  ServerScope = PR_GLOBAL_THREAD;
    539  ClientScope = PR_LOCAL_THREAD;
    540  do_work();
    541 }
    542 
    543 static void do_workKK(void) {
    544  ServerScope = PR_GLOBAL_THREAD;
    545  ClientScope = PR_GLOBAL_THREAD;
    546  do_work();
    547 }
    548 
    549 static void Measure(void (*func)(void), const char* msg) {
    550  PRIntervalTime start, stop;
    551  double d;
    552 
    553  start = PR_IntervalNow();
    554  (*func)();
    555  stop = PR_IntervalNow();
    556 
    557  d = (double)PR_IntervalToMicroseconds(stop - start);
    558 
    559  if (debug_mode) {
    560    printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
    561  }
    562 }
    563 
    564 int main(int argc, char** argv) {
    565  /* The command line argument: -d is used to determine if the test is being run
    566  in debug mode. The regress tool requires only one line output:PASS or FAIL.
    567  All of the printfs associated with this test has been handled with a if
    568  (debug_mode) test. Usage: test_name -d
    569  */
    570  PLOptStatus os;
    571  PLOptState* opt = PL_CreateOptState(argc, argv, "d");
    572  while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
    573    if (PL_OPT_BAD == os) {
    574      continue;
    575    }
    576    switch (opt->option) {
    577      case 'd': /* debug mode */
    578        debug_mode = 1;
    579        break;
    580      default:
    581        break;
    582    }
    583  }
    584  PL_DestroyOptState(opt);
    585 
    586  /* main test */
    587  if (debug_mode) {
    588    printf("Enter number of iterations: \n");
    589    scanf("%d", &_iterations);
    590    printf("Enter number of clients   : \n");
    591    scanf("%d", &_clients);
    592    printf("Enter size of client data : \n");
    593    scanf("%d", &_client_data);
    594    printf("Enter size of server data : \n");
    595    scanf("%d", &_server_data);
    596  } else {
    597    _iterations = 10;
    598    _clients = 1;
    599    _client_data = 10;
    600    _server_data = 10;
    601  }
    602 
    603  if (debug_mode) {
    604    printf("\n\n%d iterations with %d client threads.\n", _iterations,
    605           _clients);
    606    printf("Sending %d bytes of client data and %d bytes of server data\n",
    607           _client_data, _server_data);
    608  }
    609  PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
    610 
    611  ServerStateCVLock = PR_NewLock();
    612  ServerStateCV = PR_NewCondVar(ServerStateCVLock);
    613 
    614  Measure(do_workUU, "server loop user/user");
    615 #if 0
    616    Measure(do_workUK, "server loop user/kernel");
    617    Measure(do_workKU, "server loop kernel/user");
    618    Measure(do_workKK, "server loop kernel/kernel");
    619 #endif
    620 
    621  PR_Cleanup();
    622 
    623  return failed_already;
    624 }