tor-browser

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

provider.c (39661B)


      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 * Notes:
      9 * [1] lth. The call to Sleep() is a hack to get the test case to run
     10 * on Windows 95. Without it, the test case fails with an error
     11 * WSAECONNRESET following a recv() call. The error is caused by the
     12 * server side thread termination without a shutdown() or closesocket()
     13 * call. Windows docmunentation suggests that this is predicted
     14 * behavior; that other platforms get away with it is ... serindipity.
     15 * The test case should shutdown() or closesocket() before
     16 * thread termination. I didn't have time to figure out where or how
     17 * to do it. The Sleep() call inserts enough delay to allow the
     18 * client side to recv() all his data before the server side thread
     19 * terminates. Whew! ...
     20 *
     21 ** Modification History:
     22 * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
     23 *             The debug mode will print all of the printfs associated with this
     24 *test. The regress mode will be the default mode. Since the regress tool limits
     25 *           the output to a one line status:PASS or FAIL,all of the printf
     26 *statements have been handled with an if (debug_mode) statement.
     27 */
     28 
     29 #include "prclist.h"
     30 #include "prcvar.h"
     31 #include "prerror.h"
     32 #include "prinit.h"
     33 #include "prinrval.h"
     34 #include "prio.h"
     35 #include "prlock.h"
     36 #include "prlog.h"
     37 #include "prtime.h"
     38 #include "prmem.h"
     39 #include "prnetdb.h"
     40 #include "prprf.h"
     41 #include "prthread.h"
     42 
     43 #include "pprio.h"
     44 #include "primpl.h"
     45 
     46 #include "plstr.h"
     47 #include "plerror.h"
     48 #include "plgetopt.h"
     49 
     50 #include <stdlib.h>
     51 #include <string.h>
     52 
     53 #if defined(XP_UNIX)
     54 #  include <math.h>
     55 #endif
     56 
     57 /*
     58 ** This is the beginning of the test
     59 */
     60 
     61 #ifdef DEBUG
     62 #  define PORT_INC_DO +100
     63 #else
     64 #  define PORT_INC_DO
     65 #endif
     66 #ifdef IS_64
     67 #  define PORT_INC_3264 +200
     68 #else
     69 #  define PORT_INC_3264
     70 #endif
     71 
     72 #define RECV_FLAGS 0
     73 #define SEND_FLAGS 0
     74 #define BUFFER_SIZE 1024
     75 #define DEFAULT_BACKLOG 5
     76 #define DEFAULT_PORT 13000 PORT_INC_DO PORT_INC_3264
     77 #define DEFAULT_CLIENTS 1
     78 #define ALLOWED_IN_ACCEPT 1
     79 #define DEFAULT_CLIPPING 1000
     80 #define DEFAULT_WORKERS_MIN 1
     81 #define DEFAULT_WORKERS_MAX 1
     82 #define DEFAULT_SERVER "localhost"
     83 #define DEFAULT_EXECUTION_TIME 10
     84 #define DEFAULT_CLIENT_TIMEOUT 4000
     85 #define DEFAULT_SERVER_TIMEOUT 4000
     86 #define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH
     87 
     88 typedef enum CSState_e { cs_init, cs_run, cs_stop, cs_exit } CSState_t;
     89 
     90 static void PR_CALLBACK Worker(void* arg);
     91 typedef struct CSPool_s CSPool_t;
     92 typedef struct CSWorker_s CSWorker_t;
     93 typedef struct CSServer_s CSServer_t;
     94 typedef enum Verbosity {
     95  TEST_LOG_ALWAYS,
     96  TEST_LOG_ERROR,
     97  TEST_LOG_WARNING,
     98  TEST_LOG_NOTICE,
     99  TEST_LOG_INFO,
    100  TEST_LOG_STATUS,
    101  TEST_LOG_VERBOSE
    102 } Verbosity;
    103 
    104 static enum {
    105  thread_nspr,
    106  thread_pthread,
    107  thread_sproc,
    108  thread_win32
    109 } thread_provider;
    110 
    111 static PRInt32 domain = AF_INET;
    112 static PRInt32 protocol = 6; /* TCP */
    113 static PRFileDesc* debug_out = NULL;
    114 static PRBool debug_mode = PR_FALSE;
    115 static PRBool pthread_stats = PR_FALSE;
    116 static Verbosity verbosity = TEST_LOG_ALWAYS;
    117 static PRThreadScope thread_scope = PR_LOCAL_THREAD;
    118 
    119 struct CSWorker_s {
    120  PRCList element; /* list of the server's workers */
    121 
    122  PRThread* thread;   /* this worker objects thread */
    123  CSServer_t* server; /* back pointer to server structure */
    124 };
    125 
    126 struct CSPool_s {
    127  PRCondVar* exiting;
    128  PRCondVar* acceptComplete;
    129  PRUint32 accepting, active, workers;
    130 };
    131 
    132 struct CSServer_s {
    133  PRCList list; /* head of worker list */
    134 
    135  PRLock* ml;
    136  PRThread* thread; /* the main server thread */
    137  PRCondVar* stateChange;
    138 
    139  PRUint16 port;        /* port we're listening on */
    140  PRUint32 backlog;     /* size of our listener backlog */
    141  PRFileDesc* listener; /* the fd accepting connections */
    142 
    143  CSPool_t pool;   /* statistics on worker threads */
    144  CSState_t state; /* the server's state */
    145  struct           /* controlling worker counts */
    146  {
    147    PRUint32 minimum, maximum, accepting;
    148  } workers;
    149 
    150  /* statistics */
    151  PRIntervalTime started, stopped;
    152  PRUint32 operations, bytesTransferred;
    153 };
    154 
    155 typedef struct CSDescriptor_s {
    156  PRInt32 size;      /* size of transfer */
    157  char filename[60]; /* filename, null padded */
    158 } CSDescriptor_t;
    159 
    160 typedef struct CSClient_s {
    161  PRLock* ml;
    162  PRThread* thread;
    163  PRCondVar* stateChange;
    164  PRNetAddr serverAddress;
    165 
    166  CSState_t state;
    167 
    168  /* statistics */
    169  PRIntervalTime started, stopped;
    170  PRUint32 operations, bytesTransferred;
    171 } CSClient_t;
    172 
    173 #define TEST_LOG(l, p, a)                         \
    174  do {                                            \
    175    if (debug_mode || (p <= verbosity)) printf a; \
    176  } while (0)
    177 
    178 PRLogModuleInfo* cltsrv_log_file = NULL;
    179 
    180 #define MY_ASSERT(_expr) \
    181  ((_expr) ? ((void)0) : _MY_Assert(#_expr, __FILE__, __LINE__))
    182 
    183 #define TEST_ASSERT(_expr) \
    184  ((_expr) ? ((void)0) : _MY_Assert(#_expr, __FILE__, __LINE__))
    185 
    186 static void _MY_Assert(const char* s, const char* file, PRIntn ln) {
    187  PL_PrintError(NULL);
    188  PR_Assert(s, file, ln);
    189 } /* _MY_Assert */
    190 
    191 static PRBool Aborted(PRStatus rv) {
    192  return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
    193             ? PR_TRUE
    194             : PR_FALSE;
    195 }
    196 
    197 static void TimeOfDayMessage(const char* msg, PRThread* me) {
    198  char buffer[100];
    199  PRExplodedTime tod;
    200  PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod);
    201  (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod);
    202 
    203  TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS,
    204           ("%s(0x%p): %s\n", msg, me, buffer));
    205 } /* TimeOfDayMessage */
    206 
    207 static void PR_CALLBACK Client(void* arg) {
    208  PRStatus rv;
    209  PRIntn index;
    210  char buffer[1024];
    211  PRFileDesc* fd = NULL;
    212  PRUintn clipping = DEFAULT_CLIPPING;
    213  CSClient_t* client = (CSClient_t*)arg;
    214  PRThread* me = client->thread = PR_GetCurrentThread();
    215  CSDescriptor_t* descriptor = PR_NEW(CSDescriptor_t);
    216  PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT);
    217 
    218  for (index = 0; index < sizeof(buffer); ++index) {
    219    buffer[index] = (char)index;
    220  }
    221 
    222  client->started = PR_IntervalNow();
    223 
    224  PR_Lock(client->ml);
    225  client->state = cs_run;
    226  PR_NotifyCondVar(client->stateChange);
    227  PR_Unlock(client->ml);
    228 
    229  TimeOfDayMessage("Client started at", me);
    230 
    231  while (cs_run == client->state) {
    232    PRInt32 bytes, descbytes, filebytes, netbytes;
    233 
    234    (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer));
    235    TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
    236             ("\tClient(0x%p): connecting to server at %s\n", me, buffer));
    237 
    238    fd = PR_Socket(domain, SOCK_STREAM, protocol);
    239    TEST_ASSERT(NULL != fd);
    240    rv = PR_Connect(fd, &client->serverAddress, timeout);
    241    if (PR_FAILURE == rv) {
    242      TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    243               ("\tClient(0x%p): conection failed\n", me));
    244      goto aborted;
    245    }
    246 
    247    memset(descriptor, 0, sizeof(*descriptor));
    248    descriptor->size = PR_htonl(descbytes = rand() % clipping);
    249    PR_snprintf(descriptor->filename, sizeof(descriptor->filename),
    250                "CS%p%p-%p.dat", client->started, me, client->operations);
    251    TEST_LOG(
    252        cltsrv_log_file, TEST_LOG_VERBOSE,
    253        ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes));
    254    bytes = PR_Send(fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout);
    255    if (sizeof(CSDescriptor_t) != bytes) {
    256      if (Aborted(PR_FAILURE)) {
    257        goto aborted;
    258      }
    259      if (PR_IO_TIMEOUT_ERROR == PR_GetError()) {
    260        TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    261                 ("\tClient(0x%p): send descriptor timeout\n", me));
    262        goto retry;
    263      }
    264    }
    265    TEST_ASSERT(sizeof(*descriptor) == bytes);
    266 
    267    netbytes = 0;
    268    while (netbytes < descbytes) {
    269      filebytes = sizeof(buffer);
    270      if ((descbytes - netbytes) < filebytes) {
    271        filebytes = descbytes - netbytes;
    272      }
    273      TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    274               ("\tClient(0x%p): sending %d bytes\n", me, filebytes));
    275      bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
    276      if (filebytes != bytes) {
    277        if (Aborted(PR_FAILURE)) {
    278          goto aborted;
    279        }
    280        if (PR_IO_TIMEOUT_ERROR == PR_GetError()) {
    281          TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    282                   ("\tClient(0x%p): send data timeout\n", me));
    283          goto retry;
    284        }
    285      }
    286      TEST_ASSERT(bytes == filebytes);
    287      netbytes += bytes;
    288    }
    289    filebytes = 0;
    290    while (filebytes < descbytes) {
    291      netbytes = sizeof(buffer);
    292      if ((descbytes - filebytes) < netbytes) {
    293        netbytes = descbytes - filebytes;
    294      }
    295      TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    296               ("\tClient(0x%p): receiving %d bytes\n", me, netbytes));
    297      bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
    298      if (-1 == bytes) {
    299        if (Aborted(PR_FAILURE)) {
    300          TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    301                   ("\tClient(0x%p): receive data aborted\n", me));
    302          goto aborted;
    303        } else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
    304          TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    305                   ("\tClient(0x%p): receive data timeout\n", me));
    306        else
    307          TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    308                   ("\tClient(0x%p): receive error (%d, %d)\n", me,
    309                    PR_GetError(), PR_GetOSError()));
    310        goto retry;
    311      }
    312      if (0 == bytes) {
    313        TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    314                 ("\t\tClient(0x%p): unexpected end of stream\n",
    315                  PR_GetCurrentThread()));
    316        break;
    317      }
    318      filebytes += bytes;
    319    }
    320 
    321    rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
    322    if (Aborted(rv)) {
    323      goto aborted;
    324    }
    325    TEST_ASSERT(PR_SUCCESS == rv);
    326  retry:
    327    (void)PR_Close(fd);
    328    fd = NULL;
    329    TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
    330             ("\tClient(0x%p): disconnected from server\n", me));
    331 
    332    PR_Lock(client->ml);
    333    client->operations += 1;
    334    client->bytesTransferred += 2 * descbytes;
    335    rv = PR_WaitCondVar(client->stateChange, rand() % clipping);
    336    PR_Unlock(client->ml);
    337    if (Aborted(rv)) {
    338      break;
    339    }
    340  }
    341 
    342 aborted:
    343  client->stopped = PR_IntervalNow();
    344 
    345  PR_ClearInterrupt();
    346  if (NULL != fd) {
    347    rv = PR_Close(fd);
    348  }
    349 
    350  PR_Lock(client->ml);
    351  client->state = cs_exit;
    352  PR_NotifyCondVar(client->stateChange);
    353  PR_Unlock(client->ml);
    354  PR_DELETE(descriptor);
    355  TEST_LOG(
    356      cltsrv_log_file, TEST_LOG_ALWAYS,
    357      ("\tClient(0x%p): stopped after %u operations and %u bytes\n",
    358       PR_GetCurrentThread(), client->operations, client->bytesTransferred));
    359 
    360 } /* Client */
    361 
    362 static PRStatus ProcessRequest(PRFileDesc* fd, CSServer_t* server) {
    363  PRStatus drv, rv;
    364  char buffer[1024];
    365  PRFileDesc* file = NULL;
    366  PRThread* me = PR_GetCurrentThread();
    367  PRInt32 bytes, descbytes, netbytes, filebytes = 0;
    368  CSDescriptor_t* descriptor = PR_NEW(CSDescriptor_t);
    369  PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT);
    370 
    371  TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    372           ("\tProcessRequest(0x%p): receiving desciptor\n", me));
    373  bytes = PR_Recv(fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout);
    374  if (-1 == bytes) {
    375    rv = PR_FAILURE;
    376    if (Aborted(rv)) {
    377      goto exit;
    378    }
    379    if (PR_IO_TIMEOUT_ERROR == PR_GetError()) {
    380      TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    381               ("\tProcessRequest(0x%p): receive timeout\n", me));
    382    }
    383    goto exit;
    384  }
    385  if (0 == bytes) {
    386    rv = PR_FAILURE;
    387    TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    388             ("\tProcessRequest(0x%p): unexpected end of file\n", me));
    389    goto exit;
    390  }
    391  descbytes = PR_ntohl(descriptor->size);
    392  TEST_ASSERT(sizeof(*descriptor) == bytes);
    393 
    394  TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    395           ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n", me,
    396            descbytes, descriptor->filename));
    397 
    398  file = PR_Open(descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666);
    399  if (NULL == file) {
    400    rv = PR_FAILURE;
    401    if (Aborted(rv)) {
    402      goto aborted;
    403    }
    404    if (PR_IO_TIMEOUT_ERROR == PR_GetError()) {
    405      TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    406               ("\tProcessRequest(0x%p): open file timeout\n", me));
    407      goto aborted;
    408    }
    409  }
    410  TEST_ASSERT(NULL != file);
    411 
    412  filebytes = 0;
    413  while (filebytes < descbytes) {
    414    netbytes = sizeof(buffer);
    415    if ((descbytes - filebytes) < netbytes) {
    416      netbytes = descbytes - filebytes;
    417    }
    418    TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    419             ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes));
    420    bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
    421    if (-1 == bytes) {
    422      rv = PR_FAILURE;
    423      if (Aborted(rv)) {
    424        goto aborted;
    425      }
    426      if (PR_IO_TIMEOUT_ERROR == PR_GetError()) {
    427        TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    428                 ("\t\tProcessRequest(0x%p): receive data timeout\n", me));
    429        goto aborted;
    430      }
    431      /*
    432       * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
    433       * on NT here.  This is equivalent to ECONNRESET on Unix.
    434       *     -wtc
    435       */
    436      TEST_LOG(cltsrv_log_file, TEST_LOG_WARNING,
    437               ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n", me,
    438                PR_GetError(), PR_GetOSError()));
    439      goto aborted;
    440    }
    441    if (0 == bytes) {
    442      TEST_LOG(cltsrv_log_file, TEST_LOG_WARNING,
    443               ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
    444      rv = PR_FAILURE;
    445      goto aborted;
    446    }
    447    filebytes += bytes;
    448    netbytes = bytes;
    449    /* The byte count for PR_Write should be positive */
    450    MY_ASSERT(netbytes > 0);
    451    TEST_LOG(
    452        cltsrv_log_file, TEST_LOG_VERBOSE,
    453        ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes));
    454    bytes = PR_Write(file, buffer, netbytes);
    455    if (netbytes != bytes) {
    456      rv = PR_FAILURE;
    457      if (Aborted(rv)) {
    458        goto aborted;
    459      }
    460      if (PR_IO_TIMEOUT_ERROR == PR_GetError()) {
    461        TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    462                 ("\t\tProcessRequest(0x%p): write file timeout\n", me));
    463        goto aborted;
    464      }
    465    }
    466    TEST_ASSERT(bytes > 0);
    467  }
    468 
    469  PR_Lock(server->ml);
    470  server->operations += 1;
    471  server->bytesTransferred += filebytes;
    472  PR_Unlock(server->ml);
    473 
    474  rv = PR_Close(file);
    475  file = NULL;
    476  if (Aborted(rv)) {
    477    goto aborted;
    478  }
    479  TEST_ASSERT(PR_SUCCESS == rv);
    480 
    481  TEST_LOG(
    482      cltsrv_log_file, TEST_LOG_VERBOSE,
    483      ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename));
    484  file = PR_Open(descriptor->filename, PR_RDONLY, 0);
    485  if (NULL == file) {
    486    rv = PR_FAILURE;
    487    if (Aborted(rv)) {
    488      goto aborted;
    489    }
    490    if (PR_IO_TIMEOUT_ERROR == PR_GetError()) {
    491      TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    492               ("\t\tProcessRequest(0x%p): open file timeout\n",
    493                PR_GetCurrentThread()));
    494      goto aborted;
    495    }
    496    TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    497             ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n", me,
    498              PR_GetError(), PR_GetOSError()));
    499    goto aborted;
    500  }
    501  TEST_ASSERT(NULL != file);
    502 
    503  netbytes = 0;
    504  while (netbytes < descbytes) {
    505    filebytes = sizeof(buffer);
    506    if ((descbytes - netbytes) < filebytes) {
    507      filebytes = descbytes - netbytes;
    508    }
    509    TEST_LOG(
    510        cltsrv_log_file, TEST_LOG_VERBOSE,
    511        ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes));
    512    bytes = PR_Read(file, buffer, filebytes);
    513    if (filebytes != bytes) {
    514      rv = PR_FAILURE;
    515      if (Aborted(rv)) {
    516        goto aborted;
    517      }
    518      if (PR_IO_TIMEOUT_ERROR == PR_GetError())
    519        TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    520                 ("\t\tProcessRequest(0x%p): read file timeout\n", me));
    521      else
    522        TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    523                 ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n", me,
    524                  PR_GetError(), PR_GetOSError()));
    525      goto aborted;
    526    }
    527    TEST_ASSERT(bytes > 0);
    528    netbytes += bytes;
    529    filebytes = bytes;
    530    TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    531             ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes));
    532    bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
    533    if (filebytes != bytes) {
    534      rv = PR_FAILURE;
    535      if (Aborted(rv)) {
    536        goto aborted;
    537      }
    538      if (PR_IO_TIMEOUT_ERROR == PR_GetError()) {
    539        TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    540                 ("\t\tProcessRequest(0x%p): send data timeout\n", me));
    541        goto aborted;
    542      }
    543      break;
    544    }
    545    TEST_ASSERT(bytes > 0);
    546  }
    547 
    548  PR_Lock(server->ml);
    549  server->bytesTransferred += filebytes;
    550  PR_Unlock(server->ml);
    551 
    552  rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
    553  if (Aborted(rv)) {
    554    goto aborted;
    555  }
    556 
    557  rv = PR_Close(file);
    558  file = NULL;
    559  if (Aborted(rv)) {
    560    goto aborted;
    561  }
    562  TEST_ASSERT(PR_SUCCESS == rv);
    563 
    564 aborted:
    565  PR_ClearInterrupt();
    566  if (NULL != file) {
    567    PR_Close(file);
    568  }
    569  drv = PR_Delete(descriptor->filename);
    570  TEST_ASSERT(PR_SUCCESS == drv);
    571 exit:
    572  TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    573           ("\t\tProcessRequest(0x%p): Finished\n", me));
    574 
    575  PR_DELETE(descriptor);
    576 
    577 #if defined(WIN95)
    578  PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */
    579 #endif
    580  return rv;
    581 } /* ProcessRequest */
    582 
    583 typedef void (*StartFn)(void*);
    584 typedef struct StartObject {
    585  StartFn start;
    586  void* arg;
    587 } StartObject;
    588 
    589 #if defined(_PR_PTHREADS)
    590 #  include "md/_pth.h"
    591 #  include <pthread.h>
    592 
    593 static void* pthread_start(void* arg) {
    594  StartObject* so = (StartObject*)arg;
    595  StartFn start = so->start;
    596  void* data = so->arg;
    597  PR_Free(so);
    598  start(data);
    599  return NULL;
    600 } /* pthread_start */
    601 #endif /* defined(_PR_PTHREADS) */
    602 
    603 #if defined(WIN32)
    604 #  include <process.h> /* for _beginthreadex() */
    605 
    606 static PRUintn __stdcall windows_start(void* arg) {
    607  StartObject* so = (StartObject*)arg;
    608  StartFn start = so->start;
    609  void* data = so->arg;
    610  PR_Free(so);
    611  start(data);
    612  return 0;
    613 } /* windows_start */
    614 #endif /* defined(WIN32) */
    615 
    616 static PRStatus JoinThread(PRThread* thread) {
    617  PRStatus rv;
    618  switch (thread_provider) {
    619    case thread_nspr:
    620      rv = PR_JoinThread(thread);
    621      break;
    622    case thread_pthread:
    623 #if defined(_PR_PTHREADS)
    624      rv = PR_SUCCESS;
    625      break;
    626 #endif /* defined(_PR_PTHREADS) */
    627    case thread_win32:
    628 #if defined(WIN32)
    629      rv = PR_SUCCESS;
    630      break;
    631 #endif
    632    default:
    633      rv = PR_FAILURE;
    634      break;
    635  }
    636  return rv;
    637 } /* JoinThread */
    638 
    639 static PRStatus NewThread(StartFn start, void* arg, PRThreadPriority prio,
    640                          PRThreadState state) {
    641  PRStatus rv;
    642 
    643  switch (thread_provider) {
    644    case thread_nspr: {
    645      PRThread* thread =
    646          PR_CreateThread(PR_USER_THREAD, start, arg, PR_PRIORITY_NORMAL,
    647                          PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
    648      rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS;
    649    } break;
    650    case thread_pthread:
    651 #if defined(_PR_PTHREADS)
    652    {
    653      int rv;
    654      pthread_t id;
    655      pthread_attr_t tattr;
    656      StartObject* start_object;
    657      start_object = PR_NEW(StartObject);
    658      PR_ASSERT(NULL != start_object);
    659      start_object->start = start;
    660      start_object->arg = arg;
    661 
    662      rv = _PT_PTHREAD_ATTR_INIT(&tattr);
    663      PR_ASSERT(0 == rv);
    664 
    665      rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
    666      PR_ASSERT(0 == rv);
    667 
    668      rv = pthread_attr_setstacksize(&tattr, 64 * 1024);
    669      PR_ASSERT(0 == rv);
    670 
    671      rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object);
    672      (void)_PT_PTHREAD_ATTR_DESTROY(&tattr);
    673      return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
    674    }
    675 #else
    676      PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
    677      rv = PR_FAILURE;
    678 #endif /* defined(_PR_PTHREADS) */
    679    break;
    680 
    681    case thread_sproc:
    682      PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
    683      rv = PR_FAILURE;
    684      break;
    685    case thread_win32:
    686 #if defined(WIN32)
    687    {
    688      void* th;
    689      PRUintn id;
    690      StartObject* start_object;
    691      start_object = PR_NEW(StartObject);
    692      PR_ASSERT(NULL != start_object);
    693      start_object->start = start;
    694      start_object->arg = arg;
    695      th = (void*)_beginthreadex(
    696          NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes
    697                 */
    698          0U,   /* DWORD - initial thread stack size, in bytes */
    699          windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function
    700                          */
    701          start_object,  /* LPVOID - argument for new thread */
    702          STACK_SIZE_PARAM_IS_A_RESERVATION, /*DWORD dwCreationFlags - creation
    703                                                flags */
    704          &id /* LPDWORD - pointer to returned thread identifier */);
    705 
    706      rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS;
    707    }
    708 #else
    709      PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
    710      rv = PR_FAILURE;
    711 #endif
    712    break;
    713    default:
    714      PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
    715      rv = PR_FAILURE;
    716  }
    717  return rv;
    718 } /* NewThread */
    719 
    720 static PRStatus CreateWorker(CSServer_t* server, CSPool_t* pool) {
    721  PRStatus rv;
    722  CSWorker_t* worker = PR_NEWZAP(CSWorker_t);
    723  worker->server = server;
    724  PR_INIT_CLIST(&worker->element);
    725  rv = NewThread(Worker, worker, DEFAULT_SERVER_PRIORITY, PR_UNJOINABLE_THREAD);
    726  if (PR_FAILURE == rv) {
    727    PR_DELETE(worker);
    728  }
    729 
    730  TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
    731           ("\tCreateWorker(0x%p): create new worker (0x%p)\n",
    732            PR_GetCurrentThread(), worker->thread));
    733 
    734  return rv;
    735 } /* CreateWorker */
    736 
    737 static void PR_CALLBACK Worker(void* arg) {
    738  PRStatus rv;
    739  PRNetAddr from;
    740  PRFileDesc* fd = NULL;
    741  CSWorker_t* worker = (CSWorker_t*)arg;
    742  CSServer_t* server = worker->server;
    743  CSPool_t* pool = &server->pool;
    744 
    745  PRThread* me = worker->thread = PR_GetCurrentThread();
    746 
    747  TEST_LOG(cltsrv_log_file, TEST_LOG_NOTICE,
    748           ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1));
    749 
    750  PR_Lock(server->ml);
    751  PR_APPEND_LINK(&worker->element, &server->list);
    752  pool->workers += 1; /* define our existance */
    753 
    754  while (cs_run == server->state) {
    755    while (pool->accepting >= server->workers.accepting) {
    756      TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    757               ("\t\tWorker(0x%p): waiting for accept slot[%d]\n", me,
    758                pool->accepting));
    759      rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT);
    760      if (Aborted(rv) || (cs_run != server->state)) {
    761        TEST_LOG(cltsrv_log_file, TEST_LOG_NOTICE,
    762                 ("\tWorker(0x%p): has been %s\n", me,
    763                  (Aborted(rv) ? "interrupted" : "stopped")));
    764        goto exit;
    765      }
    766    }
    767    pool->accepting += 1; /* how many are really in accept */
    768    PR_Unlock(server->ml);
    769 
    770    TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    771             ("\t\tWorker(0x%p): calling accept\n", me));
    772    fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT);
    773 
    774    PR_Lock(server->ml);
    775    pool->accepting -= 1;
    776    PR_NotifyCondVar(pool->acceptComplete);
    777 
    778    if ((NULL == fd) && Aborted(PR_FAILURE)) {
    779      if (NULL != server->listener) {
    780        PR_Close(server->listener);
    781        server->listener = NULL;
    782      }
    783      goto exit;
    784    }
    785 
    786    if (NULL != fd) {
    787      /*
    788      ** Create another worker of the total number of workers is
    789      ** less than the minimum specified or we have none left in
    790      ** accept() AND we're not over the maximum.
    791      ** This sort of presumes that the number allowed in accept
    792      ** is at least as many as the minimum. Otherwise we'll keep
    793      ** creating new threads and deleting them soon after.
    794      */
    795      PRBool another = ((pool->workers < server->workers.minimum) ||
    796                        ((0 == pool->accepting) &&
    797                         (pool->workers < server->workers.maximum)))
    798                           ? PR_TRUE
    799                           : PR_FALSE;
    800      pool->active += 1;
    801      PR_Unlock(server->ml);
    802 
    803      if (another) {
    804        (void)CreateWorker(server, pool);
    805      }
    806 
    807      rv = ProcessRequest(fd, server);
    808      if (PR_SUCCESS != rv)
    809        TEST_LOG(cltsrv_log_file, TEST_LOG_ERROR,
    810                 ("\t\tWorker(0x%p): server process ended abnormally\n", me));
    811      (void)PR_Close(fd);
    812      fd = NULL;
    813 
    814      PR_Lock(server->ml);
    815      pool->active -= 1;
    816    }
    817  }
    818 
    819 exit:
    820  PR_ClearInterrupt();
    821  PR_Unlock(server->ml);
    822 
    823  if (NULL != fd) {
    824    (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
    825    (void)PR_Close(fd);
    826  }
    827 
    828  TEST_LOG(cltsrv_log_file, TEST_LOG_NOTICE,
    829           ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(),
    830            pool->workers));
    831 
    832  PR_Lock(server->ml);
    833  pool->workers -= 1; /* undefine our existance */
    834  PR_REMOVE_AND_INIT_LINK(&worker->element);
    835  PR_NotifyCondVar(pool->exiting);
    836  PR_Unlock(server->ml);
    837 
    838  PR_DELETE(worker); /* destruction of the "worker" object */
    839 
    840 } /* Worker */
    841 
    842 static void PR_CALLBACK Server(void* arg) {
    843  PRStatus rv;
    844  PRNetAddr serverAddress;
    845  CSServer_t* server = (CSServer_t*)arg;
    846  PRThread* me = server->thread = PR_GetCurrentThread();
    847  PRSocketOptionData sockOpt;
    848 
    849  server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
    850 
    851  sockOpt.option = PR_SockOpt_Reuseaddr;
    852  sockOpt.value.reuse_addr = PR_TRUE;
    853  rv = PR_SetSocketOption(server->listener, &sockOpt);
    854  TEST_ASSERT(PR_SUCCESS == rv);
    855 
    856  memset(&serverAddress, 0, sizeof(serverAddress));
    857  rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
    858 
    859  rv = PR_Bind(server->listener, &serverAddress);
    860  TEST_ASSERT(PR_SUCCESS == rv);
    861 
    862  rv = PR_Listen(server->listener, server->backlog);
    863  TEST_ASSERT(PR_SUCCESS == rv);
    864 
    865  server->started = PR_IntervalNow();
    866  TimeOfDayMessage("Server started at", me);
    867 
    868  PR_Lock(server->ml);
    869  server->state = cs_run;
    870  PR_NotifyCondVar(server->stateChange);
    871  PR_Unlock(server->ml);
    872 
    873  /*
    874  ** Create the first worker (actually, a thread that accepts
    875  ** connections and then processes the work load as needed).
    876  ** From this point on, additional worker threads are created
    877  ** as they are needed by existing worker threads.
    878  */
    879  rv = CreateWorker(server, &server->pool);
    880  TEST_ASSERT(PR_SUCCESS == rv);
    881 
    882  /*
    883  ** From here on this thread is merely hanging around as the contact
    884  ** point for the main test driver. It's just waiting for the driver
    885  ** to declare the test complete.
    886  */
    887  TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    888           ("\tServer(0x%p): waiting for state change\n", me));
    889 
    890  PR_Lock(server->ml);
    891  while ((cs_run == server->state) && !Aborted(rv)) {
    892    rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
    893  }
    894  PR_Unlock(server->ml);
    895  PR_ClearInterrupt();
    896 
    897  TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
    898           ("\tServer(0x%p): shutting down workers\n", me));
    899 
    900  /*
    901  ** Get all the worker threads to exit. They know how to
    902  ** clean up after themselves, so this is just a matter of
    903  ** waiting for clorine in the pool to take effect. During
    904  ** this stage we're ignoring interrupts.
    905  */
    906  server->workers.minimum = server->workers.maximum = 0;
    907 
    908  PR_Lock(server->ml);
    909  while (!PR_CLIST_IS_EMPTY(&server->list)) {
    910    PRCList* head = PR_LIST_HEAD(&server->list);
    911    CSWorker_t* worker = (CSWorker_t*)head;
    912    TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
    913             ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker));
    914    rv = PR_Interrupt(worker->thread);
    915    TEST_ASSERT(PR_SUCCESS == rv);
    916    PR_REMOVE_AND_INIT_LINK(head);
    917  }
    918 
    919  while (server->pool.workers > 0) {
    920    TEST_LOG(cltsrv_log_file, TEST_LOG_NOTICE,
    921             ("\tServer(0x%p): waiting for %u workers to exit\n", me,
    922              server->pool.workers));
    923    (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT);
    924  }
    925 
    926  server->state = cs_exit;
    927  PR_NotifyCondVar(server->stateChange);
    928  PR_Unlock(server->ml);
    929 
    930  TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS,
    931           ("\tServer(0x%p): stopped after %u operations and %u bytes\n", me,
    932            server->operations, server->bytesTransferred));
    933 
    934  if (NULL != server->listener) {
    935    PR_Close(server->listener);
    936  }
    937  server->stopped = PR_IntervalNow();
    938 
    939 } /* Server */
    940 
    941 static void WaitForCompletion(PRIntn execution) {
    942  while (execution > 0) {
    943    PRIntn dally = (execution > 30) ? 30 : execution;
    944    PR_Sleep(PR_SecondsToInterval(dally));
    945    if (pthread_stats) {
    946      PT_FPrintStats(debug_out, "\nPThread Statistics\n");
    947    }
    948    execution -= dally;
    949  }
    950 } /* WaitForCompletion */
    951 
    952 static void Help(void) {
    953  PR_fprintf(debug_out, "cltsrv test program usage:\n");
    954  PR_fprintf(debug_out,
    955             "\t-a <n>       threads allowed in accept        (5)\n");
    956  PR_fprintf(debug_out,
    957             "\t-b <n>       backlock for listen              (5)\n");
    958  PR_fprintf(debug_out,
    959             "\t-c <threads> number of clients to create      (1)\n");
    960  PR_fprintf(debug_out,
    961             "\t-w <threads> minimal number of server threads (1)\n");
    962  PR_fprintf(debug_out,
    963             "\t-W <threads> maximum number of server threads (1)\n");
    964  PR_fprintf(debug_out,
    965             "\t-e <seconds> duration of the test in seconds  (10)\n");
    966  PR_fprintf(debug_out,
    967             "\t-s <string>  dsn name of server               (localhost)\n");
    968  PR_fprintf(debug_out,
    969             "\t-G           use GLOBAL threads               (LOCAL)\n");
    970  PR_fprintf(debug_out,
    971             "\t-T <string>  thread provider ('n' | 'p' | 'w')(n)\n");
    972  PR_fprintf(debug_out,
    973             "\t-X           use XTP as transport             (TCP)\n");
    974  PR_fprintf(debug_out,
    975             "\t-6           Use IPv6                         (IPv4)\n");
    976  PR_fprintf(debug_out,
    977             "\t-v           verbosity (accumulative)         (0)\n");
    978  PR_fprintf(debug_out,
    979             "\t-p           pthread statistics               (FALSE)\n");
    980  PR_fprintf(debug_out,
    981             "\t-d           debug mode                       (FALSE)\n");
    982  PR_fprintf(debug_out, "\t-h           this message\n");
    983 } /* Help */
    984 
    985 static Verbosity IncrementVerbosity(void) {
    986  PRIntn verboge = (PRIntn)verbosity + 1;
    987  return (Verbosity)verboge;
    988 } /* IncrementVerbosity */
    989 
    990 int main(int argc, char** argv) {
    991  PRUintn index;
    992  PRBool boolean;
    993  CSClient_t* client;
    994  PRStatus rv, joinStatus;
    995  CSServer_t* server = NULL;
    996  char* thread_type;
    997 
    998  PRUintn backlog = DEFAULT_BACKLOG;
    999  PRUintn clients = DEFAULT_CLIENTS;
   1000  const char* serverName = DEFAULT_SERVER;
   1001  PRBool serverIsLocal = PR_TRUE;
   1002  PRUintn accepting = ALLOWED_IN_ACCEPT;
   1003  PRUintn workersMin = DEFAULT_WORKERS_MIN;
   1004  PRUintn workersMax = DEFAULT_WORKERS_MAX;
   1005  PRIntn execution = DEFAULT_EXECUTION_TIME;
   1006 
   1007  /*
   1008   * -G           use global threads
   1009   * -a <n>       threads allowed in accept
   1010   * -b <n>       backlock for listen
   1011   * -c <threads> number of clients to create
   1012   * -w <threads> minimal number of server threads
   1013   * -W <threads> maximum number of server threads
   1014   * -e <seconds> duration of the test in seconds
   1015   * -s <string>  dsn name of server (implies no server here)
   1016   * -v           verbosity
   1017   */
   1018 
   1019  PLOptStatus os;
   1020  PLOptState* opt = PL_CreateOptState(argc, argv, "GX6b:a:c:w:W:e:s:T:vdhp");
   1021 
   1022 #if defined(WIN32)
   1023  thread_provider = thread_win32;
   1024 #elif defined(_PR_PTHREADS)
   1025  thread_provider = thread_pthread;
   1026 #else
   1027  thread_provider = thread_nspr;
   1028 #endif
   1029 
   1030  debug_out = PR_GetSpecialFD(PR_StandardError);
   1031 
   1032  while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
   1033    if (PL_OPT_BAD == os) {
   1034      continue;
   1035    }
   1036    switch (opt->option) {
   1037      case 'G': /* use global threads */
   1038        thread_scope = PR_GLOBAL_THREAD;
   1039        break;
   1040      case 'X': /* use XTP as transport */
   1041        protocol = 36;
   1042        break;
   1043      case '6': /* Use IPv6 */
   1044        domain = PR_AF_INET6;
   1045        break;
   1046      case 'a': /* the value for accepting */
   1047        accepting = atoi(opt->value);
   1048        break;
   1049      case 'b': /* the value for backlock */
   1050        backlog = atoi(opt->value);
   1051        break;
   1052      case 'T': /* the thread provider */
   1053        if ('n' == *opt->value) {
   1054          thread_provider = thread_nspr;
   1055        } else if ('p' == *opt->value) {
   1056          thread_provider = thread_pthread;
   1057        } else if ('w' == *opt->value) {
   1058          thread_provider = thread_win32;
   1059        } else {
   1060          Help();
   1061          return 2;
   1062        }
   1063        break;
   1064      case 'c': /* number of client threads */
   1065        clients = atoi(opt->value);
   1066        break;
   1067      case 'w': /* minimum server worker threads */
   1068        workersMin = atoi(opt->value);
   1069        break;
   1070      case 'W': /* maximum server worker threads */
   1071        workersMax = atoi(opt->value);
   1072        break;
   1073      case 'e': /* program execution time in seconds */
   1074        execution = atoi(opt->value);
   1075        break;
   1076      case 's': /* server's address */
   1077        serverName = opt->value;
   1078        break;
   1079      case 'v': /* verbosity */
   1080        verbosity = IncrementVerbosity();
   1081        break;
   1082      case 'd': /* debug mode */
   1083        debug_mode = PR_TRUE;
   1084        break;
   1085      case 'p': /* pthread mode */
   1086        pthread_stats = PR_TRUE;
   1087        break;
   1088      case 'h':
   1089      default:
   1090        Help();
   1091        return 2;
   1092    }
   1093  }
   1094  PL_DestroyOptState(opt);
   1095 
   1096  if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) {
   1097    serverIsLocal = PR_FALSE;
   1098  }
   1099  if (0 == execution) {
   1100    execution = DEFAULT_EXECUTION_TIME;
   1101  }
   1102  if (0 == workersMax) {
   1103    workersMax = DEFAULT_WORKERS_MAX;
   1104  }
   1105  if (0 == workersMin) {
   1106    workersMin = DEFAULT_WORKERS_MIN;
   1107  }
   1108  if (0 == accepting) {
   1109    accepting = ALLOWED_IN_ACCEPT;
   1110  }
   1111  if (0 == backlog) {
   1112    backlog = DEFAULT_BACKLOG;
   1113  }
   1114 
   1115  if (workersMin > accepting) {
   1116    accepting = workersMin;
   1117  }
   1118 
   1119  TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread());
   1120 
   1121  cltsrv_log_file = PR_NewLogModule("cltsrv_log");
   1122  MY_ASSERT(NULL != cltsrv_log_file);
   1123  boolean = PR_SetLogFile("cltsrv.log");
   1124  MY_ASSERT(boolean);
   1125 
   1126  if (serverIsLocal) {
   1127    /* Establish the server */
   1128    TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
   1129             ("main(0x%p): starting server\n", PR_GetCurrentThread()));
   1130 
   1131    server = PR_NEWZAP(CSServer_t);
   1132    PR_INIT_CLIST(&server->list);
   1133    server->state = cs_init;
   1134    server->ml = PR_NewLock();
   1135    server->backlog = backlog;
   1136    server->port = DEFAULT_PORT;
   1137    server->workers.minimum = workersMin;
   1138    server->workers.maximum = workersMax;
   1139    server->workers.accepting = accepting;
   1140    server->stateChange = PR_NewCondVar(server->ml);
   1141    server->pool.exiting = PR_NewCondVar(server->ml);
   1142    server->pool.acceptComplete = PR_NewCondVar(server->ml);
   1143 
   1144    TEST_LOG(cltsrv_log_file, TEST_LOG_NOTICE,
   1145             ("main(0x%p): creating server thread\n", PR_GetCurrentThread()));
   1146 
   1147    rv = NewThread(Server, server, PR_PRIORITY_HIGH, PR_JOINABLE_THREAD);
   1148    TEST_ASSERT(PR_SUCCESS == rv);
   1149 
   1150    TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
   1151             ("main(0x%p): waiting for server init\n", PR_GetCurrentThread()));
   1152 
   1153    PR_Lock(server->ml);
   1154    while (server->state == cs_init) {
   1155      PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
   1156    }
   1157    PR_Unlock(server->ml);
   1158 
   1159    TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
   1160             ("main(0x%p): server init complete (port #%d)\n",
   1161              PR_GetCurrentThread(), server->port));
   1162  }
   1163 
   1164  if (clients != 0) {
   1165    /* Create all of the clients */
   1166    PRHostEnt host;
   1167    char buffer[BUFFER_SIZE];
   1168    client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t));
   1169 
   1170    TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
   1171             ("main(0x%p): creating %d client threads\n", PR_GetCurrentThread(),
   1172              clients));
   1173 
   1174    if (!serverIsLocal) {
   1175      rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host);
   1176      if (PR_SUCCESS != rv) {
   1177        PL_FPrintError(PR_STDERR, "PR_GetHostByName");
   1178        return 2;
   1179      }
   1180    }
   1181 
   1182    for (index = 0; index < clients; ++index) {
   1183      client[index].state = cs_init;
   1184      client[index].ml = PR_NewLock();
   1185      if (serverIsLocal) {
   1186        (void)PR_InitializeNetAddr(PR_IpAddrLoopback, DEFAULT_PORT,
   1187                                   &client[index].serverAddress);
   1188      } else {
   1189        (void)PR_EnumerateHostEnt(0, &host, DEFAULT_PORT,
   1190                                  &client[index].serverAddress);
   1191      }
   1192      client[index].stateChange = PR_NewCondVar(client[index].ml);
   1193      TEST_LOG(
   1194          cltsrv_log_file, TEST_LOG_INFO,
   1195          ("main(0x%p): creating client threads\n", PR_GetCurrentThread()));
   1196      rv = NewThread(Client, &client[index], PR_PRIORITY_NORMAL,
   1197                     PR_JOINABLE_THREAD);
   1198      TEST_ASSERT(PR_SUCCESS == rv);
   1199      PR_Lock(client[index].ml);
   1200      while (cs_init == client[index].state) {
   1201        PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
   1202      }
   1203      PR_Unlock(client[index].ml);
   1204    }
   1205  }
   1206 
   1207  /* Then just let them go at it for a bit */
   1208  TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS,
   1209           ("main(0x%p): waiting for execution interval (%d seconds)\n",
   1210            PR_GetCurrentThread(), execution));
   1211 
   1212  WaitForCompletion(execution);
   1213 
   1214  TimeOfDayMessage("Shutting down", PR_GetCurrentThread());
   1215 
   1216  if (clients != 0) {
   1217    for (index = 0; index < clients; ++index) {
   1218      TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
   1219               ("main(0x%p): notifying client(0x%p) to stop\n",
   1220                PR_GetCurrentThread(), client[index].thread));
   1221 
   1222      PR_Lock(client[index].ml);
   1223      if (cs_run == client[index].state) {
   1224        client[index].state = cs_stop;
   1225        PR_Interrupt(client[index].thread);
   1226        while (cs_stop == client[index].state)
   1227          PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
   1228      }
   1229      PR_Unlock(client[index].ml);
   1230 
   1231      TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
   1232               ("main(0x%p): joining client(0x%p)\n", PR_GetCurrentThread(),
   1233                client[index].thread));
   1234 
   1235      joinStatus = JoinThread(client[index].thread);
   1236      TEST_ASSERT(PR_SUCCESS == joinStatus);
   1237      PR_DestroyCondVar(client[index].stateChange);
   1238      PR_DestroyLock(client[index].ml);
   1239    }
   1240    PR_DELETE(client);
   1241  }
   1242 
   1243  if (NULL != server) {
   1244    /* All clients joined - retrieve the server */
   1245    TEST_LOG(cltsrv_log_file, TEST_LOG_NOTICE,
   1246             ("main(0x%p): notifying server(0x%p) to stop\n",
   1247              PR_GetCurrentThread(), server->thread));
   1248 
   1249    PR_Lock(server->ml);
   1250    server->state = cs_stop;
   1251    PR_Interrupt(server->thread);
   1252    while (cs_exit != server->state) {
   1253      PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
   1254    }
   1255    PR_Unlock(server->ml);
   1256 
   1257    TEST_LOG(cltsrv_log_file, TEST_LOG_NOTICE,
   1258             ("main(0x%p): joining server(0x%p)\n", PR_GetCurrentThread(),
   1259              server->thread));
   1260    joinStatus = JoinThread(server->thread);
   1261    TEST_ASSERT(PR_SUCCESS == joinStatus);
   1262 
   1263    PR_DestroyCondVar(server->stateChange);
   1264    PR_DestroyCondVar(server->pool.exiting);
   1265    PR_DestroyCondVar(server->pool.acceptComplete);
   1266    PR_DestroyLock(server->ml);
   1267    PR_DELETE(server);
   1268  }
   1269 
   1270  TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS,
   1271           ("main(0x%p): test complete\n", PR_GetCurrentThread()));
   1272 
   1273  if (thread_provider == thread_win32) {
   1274    thread_type = "\nWin32 Thread Statistics\n";
   1275  } else if (thread_provider == thread_pthread) {
   1276    thread_type = "\npthread Statistics\n";
   1277  } else if (thread_provider == thread_sproc) {
   1278    thread_type = "\nsproc Statistics\n";
   1279  } else {
   1280    PR_ASSERT(thread_provider == thread_nspr);
   1281    thread_type = "\nPRThread Statistics\nn";
   1282  }
   1283 
   1284  PT_FPrintStats(debug_out, thread_type);
   1285 
   1286  TimeOfDayMessage("Test exiting at", PR_GetCurrentThread());
   1287  PR_Cleanup();
   1288  return 0;
   1289 } /* main */
   1290 
   1291 /* cltsrv.c */