Back to index

lightning-sunbird  0.9+nobinonly
cltsrv.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  * Notes:
00041  * [1] lth. The call to Sleep() is a hack to get the test case to run
00042  * on Windows 95. Without it, the test case fails with an error
00043  * WSAECONNRESET following a recv() call. The error is caused by the
00044  * server side thread termination without a shutdown() or closesocket()
00045  * call. Windows docmunentation suggests that this is predicted
00046  * behavior; that other platforms get away with it is ... serindipity.
00047  * The test case should shutdown() or closesocket() before
00048  * thread termination. I didn't have time to figure out where or how
00049  * to do it. The Sleep() call inserts enough delay to allow the
00050  * client side to recv() all his data before the server side thread
00051  * terminates. Whew! ...
00052  *
00053  ** Modification History:
00054  * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
00055  *             The debug mode will print all of the printfs associated with this test.
00056  *             The regress mode will be the default mode. Since the regress tool limits
00057  *           the output to a one line status:PASS or FAIL,all of the printf statements
00058  *             have been handled with an if (debug_mode) statement. 
00059  */
00060 
00061 #include "prclist.h"
00062 #include "prcvar.h"
00063 #include "prerror.h"
00064 #include "prinit.h"
00065 #include "prinrval.h"
00066 #include "prio.h"
00067 #include "prlock.h"
00068 #include "prlog.h"
00069 #include "prtime.h"
00070 #include "prmem.h"
00071 #include "prnetdb.h"
00072 #include "prprf.h"
00073 #include "prthread.h"
00074 
00075 #include "pprio.h"
00076 #include "primpl.h"
00077 
00078 #include "plstr.h"
00079 #include "plerror.h"
00080 #include "plgetopt.h"
00081 
00082 #include <stdlib.h>
00083 #include <string.h>
00084 
00085 
00086 #if defined(XP_UNIX)
00087 #include <math.h>
00088 #endif
00089 
00090 #ifdef XP_MAC
00091 #include "prlog.h"
00092 #define printf PR_LogPrint
00093 #endif
00094 
00095 /*
00096 ** This is the beginning of the test
00097 */
00098 
00099 #define RECV_FLAGS 0
00100 #define SEND_FLAGS 0
00101 #define DEFAULT_LOW 0
00102 #define DEFAULT_HIGH 0
00103 #define BUFFER_SIZE 1024
00104 #define DEFAULT_BACKLOG 5
00105 #define DEFAULT_PORT 12849
00106 #define DEFAULT_CLIENTS 1
00107 #define ALLOWED_IN_ACCEPT 1
00108 #define DEFAULT_CLIPPING 1000
00109 #define DEFAULT_WORKERS_MIN 1
00110 #define DEFAULT_WORKERS_MAX 1
00111 #define DEFAULT_SERVER "localhost"
00112 #define DEFAULT_EXECUTION_TIME 10
00113 #define DEFAULT_CLIENT_TIMEOUT 4000
00114 #define DEFAULT_SERVER_TIMEOUT 4000
00115 #define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH
00116 
00117 typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t;
00118 
00119 static void PR_CALLBACK Worker(void *arg);
00120 typedef struct CSPool_s CSPool_t;
00121 typedef struct CSWorker_s CSWorker_t;
00122 typedef struct CSServer_s CSServer_t;
00123 typedef enum Verbosity
00124 {
00125     TEST_LOG_ALWAYS,
00126     TEST_LOG_ERROR,
00127     TEST_LOG_WARNING,
00128     TEST_LOG_NOTICE,
00129     TEST_LOG_INFO,
00130     TEST_LOG_STATUS,
00131     TEST_LOG_VERBOSE
00132 } Verbosity;
00133 
00134 static PRInt32 domain = AF_INET;
00135 static PRInt32 protocol = 6;  /* TCP */
00136 static PRFileDesc *debug_out = NULL;
00137 static PRBool debug_mode = PR_FALSE;
00138 static PRBool pthread_stats = PR_FALSE;
00139 static Verbosity verbosity = TEST_LOG_ALWAYS;
00140 static PRThreadScope thread_scope = PR_LOCAL_THREAD;
00141 
00142 struct CSWorker_s
00143 {
00144     PRCList element;        /* list of the server's workers */
00145 
00146     PRThread *thread;       /* this worker objects thread */
00147     CSServer_t *server;     /* back pointer to server structure */
00148 };
00149 
00150 struct CSPool_s
00151 {
00152     PRCondVar *exiting;
00153     PRCondVar *acceptComplete;
00154     PRUint32 accepting, active, workers;
00155 };
00156 
00157 struct CSServer_s
00158 {
00159     PRCList list;           /* head of worker list */
00160 
00161     PRLock *ml;
00162     PRThread *thread;       /* the main server thread */
00163     PRCondVar *stateChange;
00164 
00165     PRUint16 port;          /* port we're listening on */
00166     PRUint32 backlog;       /* size of our listener backlog */
00167     PRFileDesc *listener;   /* the fd accepting connections */
00168 
00169     CSPool_t pool;          /* statistics on worker threads */
00170     CSState_t state;        /* the server's state */
00171     struct                  /* controlling worker counts */
00172     {
00173         PRUint32 minimum, maximum, accepting;
00174     } workers;
00175 
00176     /* statistics */
00177     PRIntervalTime started, stopped;
00178     PRUint32 operations, bytesTransferred;
00179 };
00180 
00181 typedef struct CSDescriptor_s
00182 {
00183     PRInt32 size;       /* size of transfer */
00184     char filename[60];  /* filename, null padded */
00185 } CSDescriptor_t;
00186 
00187 typedef struct CSClient_s
00188 {
00189     PRLock *ml;
00190     PRThread *thread;
00191     PRCondVar *stateChange;
00192     PRNetAddr serverAddress;
00193 
00194     CSState_t state;
00195 
00196     /* statistics */
00197     PRIntervalTime started, stopped;
00198     PRUint32 operations, bytesTransferred;
00199 } CSClient_t;
00200 
00201 #define TEST_LOG(l, p, a) \
00202     do { \
00203         if (debug_mode || (p <= verbosity)) printf a; \
00204     } while (0)
00205 
00206 PRLogModuleInfo *cltsrv_log_file = NULL;
00207 
00208 #define MY_ASSERT(_expr) \
00209     ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
00210 
00211 #define TEST_ASSERT(_expr) \
00212     ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
00213 
00214 static void _MY_Assert(const char *s, const char *file, PRIntn ln)
00215 {
00216     PL_PrintError(NULL);
00217 #if DEBUG
00218     PR_Assert(s, file, ln);
00219 #endif
00220 }  /* _MW_Assert */
00221 
00222 static PRBool Aborted(PRStatus rv)
00223 {
00224     return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ?
00225         PR_TRUE : PR_FALSE;
00226 }
00227 
00228 static void TimeOfDayMessage(const char *msg, PRThread* me)
00229 {
00230     char buffer[100];
00231     PRExplodedTime tod;
00232     PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod);
00233     (void)PR_FormatTime(buffer, sizeof(buffer), "%T", &tod);
00234 
00235     TEST_LOG(
00236         cltsrv_log_file, TEST_LOG_ALWAYS,
00237         ("%s(0x%p): %s\n", msg, me, buffer));
00238 }  /* TimeOfDayMessage */
00239 
00240 
00241 static void PR_CALLBACK Client(void *arg)
00242 {
00243     PRStatus rv;
00244     PRIntn index;
00245     char buffer[1024];
00246     PRFileDesc *fd = NULL;
00247     PRUintn clipping = DEFAULT_CLIPPING;
00248     PRThread *me = PR_CurrentThread();
00249     CSClient_t *client = (CSClient_t*)arg;
00250     CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
00251     PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT);
00252 
00253 
00254     for (index = 0; index < sizeof(buffer); ++index)
00255         buffer[index] = (char)index;
00256 
00257     client->started = PR_IntervalNow();
00258 
00259     PR_Lock(client->ml);
00260     client->state = cs_run;
00261     PR_NotifyCondVar(client->stateChange);
00262     PR_Unlock(client->ml);
00263 
00264     TimeOfDayMessage("Client started at", me);
00265 
00266     while (cs_run == client->state)
00267     {
00268         PRInt32 bytes, descbytes, filebytes, netbytes;
00269 
00270         (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer));
00271         TEST_LOG(cltsrv_log_file, TEST_LOG_INFO, 
00272             ("\tClient(0x%p): connecting to server at %s\n", me, buffer));
00273 
00274         fd = PR_Socket(domain, SOCK_STREAM, protocol);
00275         TEST_ASSERT(NULL != fd);
00276         rv = PR_Connect(fd, &client->serverAddress, timeout);
00277         if (PR_FAILURE == rv)
00278         {
00279             TEST_LOG(
00280                 cltsrv_log_file, TEST_LOG_ERROR,
00281                 ("\tClient(0x%p): conection failed (%d, %d)\n",
00282                 me, PR_GetError(), PR_GetOSError()));
00283             goto aborted;
00284         }
00285 
00286         memset(descriptor, 0, sizeof(*descriptor));
00287         descriptor->size = PR_htonl(descbytes = rand() % clipping);
00288         PR_snprintf(
00289             descriptor->filename, sizeof(descriptor->filename),
00290             "CS%p%p-%p.dat", client->started, me, client->operations);
00291         TEST_LOG(
00292             cltsrv_log_file, TEST_LOG_VERBOSE,
00293             ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes));
00294         bytes = PR_Send(
00295             fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout);
00296         if (sizeof(CSDescriptor_t) != bytes)
00297         {
00298             if (Aborted(PR_FAILURE)) goto aborted;
00299             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00300             {
00301                 TEST_LOG(
00302                     cltsrv_log_file, TEST_LOG_ERROR,
00303                     ("\tClient(0x%p): send descriptor timeout\n", me));
00304                 goto retry;
00305             }
00306         }
00307         TEST_ASSERT(sizeof(*descriptor) == bytes);
00308 
00309         netbytes = 0;
00310         while (netbytes < descbytes)
00311         {
00312             filebytes = sizeof(buffer);
00313             if ((descbytes - netbytes) < filebytes)
00314                 filebytes = descbytes - netbytes;
00315             TEST_LOG(
00316                 cltsrv_log_file, TEST_LOG_VERBOSE,
00317                 ("\tClient(0x%p): sending %d bytes\n", me, filebytes));
00318             bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
00319             if (filebytes != bytes)
00320             {
00321                 if (Aborted(PR_FAILURE)) goto aborted;
00322                 if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00323                 {
00324                     TEST_LOG(
00325                         cltsrv_log_file, TEST_LOG_ERROR,
00326                         ("\tClient(0x%p): send data timeout\n", me));
00327                     goto retry;
00328                 }
00329             }
00330             TEST_ASSERT(bytes == filebytes);
00331             netbytes += bytes;
00332         }
00333         filebytes = 0;
00334         while (filebytes < descbytes)
00335         {
00336             netbytes = sizeof(buffer);
00337             if ((descbytes - filebytes) < netbytes)
00338                 netbytes = descbytes - filebytes;
00339             TEST_LOG(
00340                 cltsrv_log_file, TEST_LOG_VERBOSE,
00341                 ("\tClient(0x%p): receiving %d bytes\n", me, netbytes));
00342             bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
00343             if (-1 == bytes)
00344             {
00345                 if (Aborted(PR_FAILURE))
00346                 {
00347                     TEST_LOG(
00348                         cltsrv_log_file, TEST_LOG_ERROR,
00349                         ("\tClient(0x%p): receive data aborted\n", me));
00350                     goto aborted;
00351                 }
00352                 else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00353                     TEST_LOG(
00354                         cltsrv_log_file, TEST_LOG_ERROR,
00355                         ("\tClient(0x%p): receive data timeout\n", me));
00356                             else
00357                     TEST_LOG(
00358                         cltsrv_log_file, TEST_LOG_ERROR,
00359                         ("\tClient(0x%p): receive error (%d, %d)\n",
00360                                           me, PR_GetError(), PR_GetOSError()));
00361                 goto retry;
00362            }
00363             if (0 == bytes)
00364             {
00365                 TEST_LOG(
00366                     cltsrv_log_file, TEST_LOG_ERROR,
00367                     ("\t\tClient(0x%p): unexpected end of stream\n",
00368                     PR_CurrentThread()));
00369                 break;
00370             }
00371             filebytes += bytes;
00372         }
00373 
00374         rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
00375         if (Aborted(rv)) goto aborted;
00376         TEST_ASSERT(PR_SUCCESS == rv);
00377 retry:
00378         (void)PR_Close(fd); fd = NULL;
00379         TEST_LOG(
00380             cltsrv_log_file, TEST_LOG_INFO,
00381             ("\tClient(0x%p): disconnected from server\n", me));
00382 
00383         PR_Lock(client->ml);
00384         client->operations += 1;
00385         client->bytesTransferred += 2 * descbytes;
00386         rv = PR_WaitCondVar(client->stateChange, rand() % clipping);
00387         PR_Unlock(client->ml);
00388         if (Aborted(rv)) break;
00389     }
00390 
00391 aborted:
00392     client->stopped = PR_IntervalNow();
00393 
00394     PR_ClearInterrupt();
00395     if (NULL != fd) rv = PR_Close(fd);
00396 
00397     PR_Lock(client->ml);
00398     client->state = cs_exit;
00399     PR_NotifyCondVar(client->stateChange);
00400     PR_Unlock(client->ml);
00401     PR_DELETE(descriptor);
00402     TEST_LOG(
00403         cltsrv_log_file, TEST_LOG_ALWAYS,
00404         ("\tClient(0x%p): stopped after %u operations and %u bytes\n",
00405         PR_CurrentThread(), client->operations, client->bytesTransferred));
00406 
00407 }  /* Client */
00408 
00409 static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server)
00410 {
00411     PRStatus drv, rv;
00412     char buffer[1024];
00413     PRFileDesc *file = NULL;
00414     PRThread * me = PR_CurrentThread();
00415     PRInt32 bytes, descbytes, netbytes, filebytes = 0;
00416     CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
00417     PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT);
00418 
00419     TEST_LOG(
00420         cltsrv_log_file, TEST_LOG_VERBOSE,
00421         ("\tProcessRequest(0x%p): receiving desciptor\n", me));
00422     bytes = PR_Recv(
00423         fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout);
00424     if (-1 == bytes)
00425     {
00426         rv = PR_FAILURE;
00427         if (Aborted(rv)) goto exit;
00428         if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00429         {
00430             TEST_LOG(
00431                 cltsrv_log_file, TEST_LOG_ERROR,
00432                 ("\tProcessRequest(0x%p): receive timeout\n", me));
00433         }
00434         goto exit;
00435     }
00436     if (0 == bytes)
00437     {
00438         rv = PR_FAILURE;
00439         TEST_LOG(
00440             cltsrv_log_file, TEST_LOG_ERROR,
00441             ("\tProcessRequest(0x%p): unexpected end of file\n", me));
00442         goto exit;
00443     }
00444     descbytes = PR_ntohl(descriptor->size);
00445     TEST_ASSERT(sizeof(*descriptor) == bytes);
00446 
00447     TEST_LOG(
00448         cltsrv_log_file, TEST_LOG_VERBOSE, 
00449         ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n",
00450         me, descbytes, descriptor->filename));
00451 
00452     file = PR_Open(
00453         descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666);
00454     if (NULL == file)
00455     {
00456         rv = PR_FAILURE;
00457         if (Aborted(rv)) goto aborted;
00458         if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00459         {
00460             TEST_LOG(
00461                 cltsrv_log_file, TEST_LOG_ERROR,
00462                 ("\tProcessRequest(0x%p): open file timeout\n", me));
00463             goto aborted;
00464         }
00465     }
00466     TEST_ASSERT(NULL != file);
00467 
00468     filebytes = 0;
00469     while (filebytes < descbytes)
00470     {
00471         netbytes = sizeof(buffer);
00472         if ((descbytes - filebytes) < netbytes)
00473             netbytes = descbytes - filebytes;
00474         TEST_LOG(
00475             cltsrv_log_file, TEST_LOG_VERBOSE,
00476             ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes));
00477         bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
00478         if (-1 == bytes)
00479         {
00480             rv = PR_FAILURE;
00481             if (Aborted(rv)) goto aborted;
00482             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00483             {
00484                 TEST_LOG(
00485                     cltsrv_log_file, TEST_LOG_ERROR,
00486                     ("\t\tProcessRequest(0x%p): receive data timeout\n", me));
00487                 goto aborted;
00488             }
00489             /*
00490              * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
00491              * on NT here.  This is equivalent to ECONNRESET on Unix.
00492              *     -wtc
00493              */
00494             TEST_LOG(
00495                 cltsrv_log_file, TEST_LOG_WARNING,
00496                 ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n",
00497                 me, PR_GetError(), PR_GetOSError()));
00498             goto aborted;
00499         }
00500         if(0 == bytes)
00501         {
00502             TEST_LOG(
00503                 cltsrv_log_file, TEST_LOG_WARNING,
00504                 ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
00505             rv = PR_FAILURE;
00506             goto aborted;
00507         }
00508         filebytes += bytes;
00509         netbytes = bytes;
00510         /* The byte count for PR_Write should be positive */
00511         MY_ASSERT(netbytes > 0);
00512         TEST_LOG(
00513             cltsrv_log_file, TEST_LOG_VERBOSE,
00514             ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes));
00515         bytes = PR_Write(file, buffer, netbytes);
00516         if (netbytes != bytes)
00517         {
00518             rv = PR_FAILURE;
00519             if (Aborted(rv)) goto aborted;
00520             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00521             {
00522                 TEST_LOG(
00523                     cltsrv_log_file, TEST_LOG_ERROR,
00524                     ("\t\tProcessRequest(0x%p): write file timeout\n", me));
00525                 goto aborted;
00526             }
00527         }
00528         TEST_ASSERT(bytes > 0);
00529     }
00530 
00531     PR_Lock(server->ml);
00532     server->operations += 1;
00533     server->bytesTransferred += filebytes;
00534     PR_Unlock(server->ml);
00535 
00536     rv = PR_Close(file);
00537     if (Aborted(rv)) goto aborted;
00538     TEST_ASSERT(PR_SUCCESS == rv);
00539     file = NULL;
00540 
00541     TEST_LOG(
00542         cltsrv_log_file, TEST_LOG_VERBOSE,
00543         ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename));
00544     file = PR_Open(descriptor->filename, PR_RDONLY, 0);
00545     if (NULL == file)
00546     {
00547         rv = PR_FAILURE;
00548         if (Aborted(rv)) goto aborted;
00549         if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00550         {
00551             TEST_LOG(
00552                 cltsrv_log_file, TEST_LOG_ERROR,
00553                 ("\t\tProcessRequest(0x%p): open file timeout\n",
00554                 PR_CurrentThread()));
00555             goto aborted;
00556         }
00557         TEST_LOG(
00558             cltsrv_log_file, TEST_LOG_ERROR,
00559             ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n",
00560             me, PR_GetError(), PR_GetOSError()));
00561         goto aborted;
00562     }
00563     TEST_ASSERT(NULL != file);
00564 
00565     netbytes = 0;
00566     while (netbytes < descbytes)
00567     {
00568         filebytes = sizeof(buffer);
00569         if ((descbytes - netbytes) < filebytes)
00570             filebytes = descbytes - netbytes;
00571         TEST_LOG(
00572             cltsrv_log_file, TEST_LOG_VERBOSE,
00573             ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes));
00574         bytes = PR_Read(file, buffer, filebytes);
00575         if (filebytes != bytes)
00576         {
00577             rv = PR_FAILURE;
00578             if (Aborted(rv)) goto aborted;
00579             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00580                 TEST_LOG(
00581                     cltsrv_log_file, TEST_LOG_ERROR,
00582                     ("\t\tProcessRequest(0x%p): read file timeout\n", me));
00583             else
00584                 TEST_LOG(
00585                     cltsrv_log_file, TEST_LOG_ERROR,
00586                     ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n",
00587                     me, PR_GetError(), PR_GetOSError()));
00588             goto aborted;
00589         }
00590         TEST_ASSERT(bytes > 0);
00591         netbytes += bytes;
00592         filebytes = bytes;
00593         TEST_LOG(
00594             cltsrv_log_file, TEST_LOG_VERBOSE,
00595             ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes));
00596         bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
00597         if (filebytes != bytes)
00598         {
00599             rv = PR_FAILURE;
00600             if (Aborted(rv)) goto aborted;
00601             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00602             {
00603                 TEST_LOG(
00604                     cltsrv_log_file, TEST_LOG_ERROR,
00605                     ("\t\tProcessRequest(0x%p): send data timeout\n", me));
00606                 goto aborted;
00607             }
00608             break;
00609         }
00610        TEST_ASSERT(bytes > 0);
00611     }
00612     
00613     PR_Lock(server->ml);
00614     server->bytesTransferred += filebytes;
00615     PR_Unlock(server->ml);
00616 
00617     rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
00618     if (Aborted(rv)) goto aborted;
00619 
00620     rv = PR_Close(file);
00621     if (Aborted(rv)) goto aborted;
00622     TEST_ASSERT(PR_SUCCESS == rv);
00623     file = NULL;
00624 
00625 aborted:
00626     PR_ClearInterrupt();
00627     if (NULL != file) PR_Close(file);
00628     drv = PR_Delete(descriptor->filename);
00629     TEST_ASSERT(PR_SUCCESS == drv);
00630 exit:
00631     TEST_LOG(
00632         cltsrv_log_file, TEST_LOG_VERBOSE,
00633         ("\t\tProcessRequest(0x%p): Finished\n", me));
00634 
00635     PR_DELETE(descriptor);
00636 
00637 #if defined(WIN95)
00638     PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */
00639 #endif
00640     return rv;
00641 }  /* ProcessRequest */
00642 
00643 static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool)
00644 {
00645     CSWorker_t *worker = PR_NEWZAP(CSWorker_t);
00646     worker->server = server;
00647     PR_INIT_CLIST(&worker->element);
00648     worker->thread = PR_CreateThread(
00649         PR_USER_THREAD, Worker, worker,
00650         DEFAULT_SERVER_PRIORITY, thread_scope,
00651         PR_UNJOINABLE_THREAD, 0);
00652     if (NULL == worker->thread)
00653     {
00654         PR_DELETE(worker);
00655         return PR_FAILURE;
00656     }
00657 
00658     TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, 
00659         ("\tCreateWorker(0x%p): create new worker (0x%p)\n",
00660         PR_CurrentThread(), worker->thread));
00661 
00662     return PR_SUCCESS;
00663 }  /* CreateWorker */
00664 
00665 static void PR_CALLBACK Worker(void *arg)
00666 {
00667     PRStatus rv;
00668     PRNetAddr from;
00669     PRFileDesc *fd = NULL;
00670     PRThread *me = PR_CurrentThread();
00671     CSWorker_t *worker = (CSWorker_t*)arg;
00672     CSServer_t *server = worker->server;
00673     CSPool_t *pool = &server->pool;
00674 
00675     TEST_LOG(
00676         cltsrv_log_file, TEST_LOG_NOTICE,
00677         ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1));
00678 
00679     PR_Lock(server->ml);
00680     PR_APPEND_LINK(&worker->element, &server->list);
00681     pool->workers += 1;  /* define our existance */
00682 
00683     while (cs_run == server->state)
00684     {
00685         while (pool->accepting >= server->workers.accepting)
00686         {
00687             TEST_LOG(
00688                 cltsrv_log_file, TEST_LOG_VERBOSE,
00689                 ("\t\tWorker(0x%p): waiting for accept slot[%d]\n",
00690                 me, pool->accepting));
00691             rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT);
00692             if (Aborted(rv) || (cs_run != server->state))
00693             {
00694                 TEST_LOG(
00695                     cltsrv_log_file, TEST_LOG_NOTICE,
00696                     ("\tWorker(0x%p): has been %s\n",
00697                     me, (Aborted(rv) ? "interrupted" : "stopped")));
00698                 goto exit;
00699             }
00700         } 
00701         pool->accepting += 1;  /* how many are really in accept */
00702         PR_Unlock(server->ml);
00703 
00704         TEST_LOG(
00705             cltsrv_log_file, TEST_LOG_VERBOSE,
00706             ("\t\tWorker(0x%p): calling accept\n", me));
00707         fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT);
00708 
00709         PR_Lock(server->ml);        
00710         pool->accepting -= 1;
00711         PR_NotifyCondVar(pool->acceptComplete);
00712 
00713         if ((NULL == fd) && Aborted(PR_FAILURE))
00714         {
00715             if (NULL != server->listener)
00716             {
00717                 PR_Close(server->listener);
00718                 server->listener = NULL;
00719             }
00720             goto exit;
00721         }
00722 
00723         if (NULL != fd)
00724         {
00725             /*
00726             ** Create another worker of the total number of workers is
00727             ** less than the minimum specified or we have none left in
00728             ** accept() AND we're not over the maximum.
00729             ** This sort of presumes that the number allowed in accept
00730             ** is at least as many as the minimum. Otherwise we'll keep
00731             ** creating new threads and deleting them soon after.
00732             */
00733             PRBool another =
00734                 ((pool->workers < server->workers.minimum) ||
00735                 ((0 == pool->accepting)
00736                     && (pool->workers < server->workers.maximum))) ?
00737                     PR_TRUE : PR_FALSE;
00738             pool->active += 1;
00739             PR_Unlock(server->ml);
00740 
00741             if (another) (void)CreateWorker(server, pool);
00742 
00743             rv = ProcessRequest(fd, server);
00744             if (PR_SUCCESS != rv)
00745                 TEST_LOG(
00746                     cltsrv_log_file, TEST_LOG_ERROR,
00747                     ("\t\tWorker(0x%p): server process ended abnormally\n", me));
00748             (void)PR_Close(fd); fd = NULL;
00749 
00750             PR_Lock(server->ml);
00751             pool->active -= 1;
00752         }
00753     }
00754 
00755 exit:
00756     PR_ClearInterrupt();    
00757     PR_Unlock(server->ml);
00758 
00759     if (NULL != fd)
00760     {
00761         (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
00762         (void)PR_Close(fd);
00763     }
00764 
00765     TEST_LOG(
00766         cltsrv_log_file, TEST_LOG_NOTICE,
00767         ("\t\tWorker(0x%p): exiting [%u]\n", PR_CurrentThread(), pool->workers));
00768 
00769     PR_Lock(server->ml);
00770     pool->workers -= 1;  /* undefine our existance */
00771     PR_REMOVE_AND_INIT_LINK(&worker->element);
00772     PR_NotifyCondVar(pool->exiting);
00773     PR_Unlock(server->ml);
00774 
00775     PR_DELETE(worker);  /* destruction of the "worker" object */
00776 
00777 }  /* Worker */
00778 
00779 static void PR_CALLBACK Server(void *arg)
00780 {
00781     PRStatus rv;
00782     PRNetAddr serverAddress;
00783     PRThread *me = PR_CurrentThread();
00784     CSServer_t *server = (CSServer_t*)arg;
00785     PRSocketOptionData sockOpt;
00786 
00787     server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
00788 
00789     sockOpt.option = PR_SockOpt_Reuseaddr;
00790     sockOpt.value.reuse_addr = PR_TRUE;
00791     rv = PR_SetSocketOption(server->listener, &sockOpt);
00792     TEST_ASSERT(PR_SUCCESS == rv);
00793 
00794     memset(&serverAddress, 0, sizeof(serverAddress));
00795        if (PR_AF_INET6 != domain)
00796               rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
00797        else
00798               rv = PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, DEFAULT_PORT,
00799                                                                                            &serverAddress);
00800     rv = PR_Bind(server->listener, &serverAddress);
00801     TEST_ASSERT(PR_SUCCESS == rv);
00802 
00803     rv = PR_Listen(server->listener, server->backlog);
00804     TEST_ASSERT(PR_SUCCESS == rv);
00805 
00806     server->started = PR_IntervalNow();
00807     TimeOfDayMessage("Server started at", me);
00808 
00809     PR_Lock(server->ml);
00810     server->state = cs_run;
00811     PR_NotifyCondVar(server->stateChange);
00812     PR_Unlock(server->ml);
00813 
00814     /*
00815     ** Create the first worker (actually, a thread that accepts
00816     ** connections and then processes the work load as needed).
00817     ** From this point on, additional worker threads are created
00818     ** as they are needed by existing worker threads.
00819     */
00820     rv = CreateWorker(server, &server->pool);
00821     TEST_ASSERT(PR_SUCCESS == rv);
00822 
00823     /*
00824     ** From here on this thread is merely hanging around as the contact
00825     ** point for the main test driver. It's just waiting for the driver
00826     ** to declare the test complete.
00827     */
00828     TEST_LOG(
00829         cltsrv_log_file, TEST_LOG_VERBOSE,
00830         ("\tServer(0x%p): waiting for state change\n", me));
00831 
00832     PR_Lock(server->ml);
00833     while ((cs_run == server->state) && !Aborted(rv))
00834     {
00835         rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
00836     }
00837     PR_Unlock(server->ml);
00838     PR_ClearInterrupt();
00839 
00840     TEST_LOG(
00841         cltsrv_log_file, TEST_LOG_INFO,
00842         ("\tServer(0x%p): shutting down workers\n", me));
00843 
00844     /*
00845     ** Get all the worker threads to exit. They know how to
00846     ** clean up after themselves, so this is just a matter of
00847     ** waiting for clorine in the pool to take effect. During
00848     ** this stage we're ignoring interrupts.
00849     */
00850     server->workers.minimum = server->workers.maximum = 0;
00851 
00852     PR_Lock(server->ml);
00853     while (!PR_CLIST_IS_EMPTY(&server->list))
00854     {
00855         PRCList *head = PR_LIST_HEAD(&server->list);
00856         CSWorker_t *worker = (CSWorker_t*)head;
00857         TEST_LOG(
00858             cltsrv_log_file, TEST_LOG_VERBOSE,
00859             ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker));
00860         rv = PR_Interrupt(worker->thread);
00861         TEST_ASSERT(PR_SUCCESS == rv);
00862         PR_REMOVE_AND_INIT_LINK(head);
00863     }
00864 
00865     while (server->pool.workers > 0)
00866     {
00867         TEST_LOG(
00868             cltsrv_log_file, TEST_LOG_NOTICE,
00869             ("\tServer(0x%p): waiting for %u workers to exit\n",
00870             me, server->pool.workers));
00871         (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT);
00872     }
00873 
00874     server->state = cs_exit;
00875     PR_NotifyCondVar(server->stateChange);
00876     PR_Unlock(server->ml);
00877 
00878     TEST_LOG(
00879         cltsrv_log_file, TEST_LOG_ALWAYS,
00880         ("\tServer(0x%p): stopped after %u operations and %u bytes\n",
00881         me, server->operations, server->bytesTransferred));
00882 
00883     if (NULL != server->listener) PR_Close(server->listener);
00884     server->stopped = PR_IntervalNow();
00885 
00886 }  /* Server */
00887 
00888 static void WaitForCompletion(PRIntn execution)
00889 {
00890     while (execution > 0)
00891     { 
00892         PRIntn dally = (execution > 30) ? 30 : execution;
00893         PR_Sleep(PR_SecondsToInterval(dally));
00894         if (pthread_stats) PT_FPrintStats(debug_out, "\nPThread Statistics\n");
00895         execution -= dally;
00896     }
00897 }  /* WaitForCompletion */
00898 
00899 static void Help(void)
00900 {
00901     PR_fprintf(debug_out, "cltsrv test program usage:\n");
00902     PR_fprintf(debug_out, "\t-a <n>       threads allowed in accept        (5)\n");
00903     PR_fprintf(debug_out, "\t-b <n>       backlock for listen              (5)\n");
00904     PR_fprintf(debug_out, "\t-c <threads> number of clients to create      (1)\n");
00905     PR_fprintf(debug_out, "\t-f <low>     low water mark for fd caching    (0)\n");
00906     PR_fprintf(debug_out, "\t-F <high>    high water mark for fd caching   (0)\n");
00907     PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n");
00908     PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n");
00909     PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds  (10)\n");
00910     PR_fprintf(debug_out, "\t-s <string>  dsn name of server               (localhost)\n");
00911     PR_fprintf(debug_out, "\t-G           use GLOBAL threads               (LOCAL)\n");
00912     PR_fprintf(debug_out, "\t-X           use XTP as transport             (TCP)\n");
00913     PR_fprintf(debug_out, "\t-6           Use IPv6                         (IPv4)\n");
00914     PR_fprintf(debug_out, "\t-v           verbosity (accumulative)         (0)\n");
00915     PR_fprintf(debug_out, "\t-p           pthread statistics               (FALSE)\n");
00916     PR_fprintf(debug_out, "\t-d           debug mode                       (FALSE)\n");
00917     PR_fprintf(debug_out, "\t-h           this message\n");
00918 }  /* Help */
00919 
00920 static Verbosity IncrementVerbosity(void)
00921 {
00922     PRIntn verboge = (PRIntn)verbosity + 1;
00923     return (Verbosity)verboge;
00924 }  /* IncrementVerbosity */
00925 
00926 PRIntn main(PRIntn argc, char** argv)
00927 {
00928     PRUintn index;
00929     PRBool boolean;
00930     CSClient_t *client;
00931     PRStatus rv, joinStatus;
00932     CSServer_t *server = NULL;
00933 
00934     PRUintn backlog = DEFAULT_BACKLOG;
00935     PRUintn clients = DEFAULT_CLIENTS;
00936     const char *serverName = DEFAULT_SERVER;
00937     PRBool serverIsLocal = PR_TRUE;
00938     PRUintn accepting = ALLOWED_IN_ACCEPT;
00939     PRUintn workersMin = DEFAULT_WORKERS_MIN;
00940     PRUintn workersMax = DEFAULT_WORKERS_MAX;
00941     PRIntn execution = DEFAULT_EXECUTION_TIME;
00942     PRIntn low = DEFAULT_LOW, high = DEFAULT_HIGH;
00943 
00944     /*
00945      * -G           use global threads
00946      * -a <n>       threads allowed in accept
00947      * -b <n>       backlock for listen
00948      * -c <threads> number of clients to create
00949      * -f <low>     low water mark for caching FDs
00950      * -F <high>    high water mark for caching FDs
00951      * -w <threads> minimal number of server threads
00952      * -W <threads> maximum number of server threads
00953      * -e <seconds> duration of the test in seconds
00954      * -s <string>  dsn name of server (implies no server here)
00955      * -v           verbosity
00956      */
00957 
00958     PLOptStatus os;
00959     PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:f:F:w:W:e:s:vdhp");
00960 
00961     debug_out = PR_GetSpecialFD(PR_StandardError);
00962 
00963     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
00964     {
00965         if (PL_OPT_BAD == os) continue;
00966         switch (opt->option)
00967         {
00968         case 'G':  /* use global threads */
00969             thread_scope = PR_GLOBAL_THREAD;
00970             break;
00971         case 'X':  /* use XTP as transport */
00972             protocol = 36;
00973             break;
00974         case '6':  /* Use IPv6 */
00975             domain = PR_AF_INET6;
00976             break;
00977         case 'a':  /* the value for accepting */
00978             accepting = atoi(opt->value);
00979             break;
00980         case 'b':  /* the value for backlock */
00981             backlog = atoi(opt->value);
00982             break;
00983         case 'c':  /* number of client threads */
00984             clients = atoi(opt->value);
00985             break;
00986         case 'f':  /* low water fd cache */
00987             low = atoi(opt->value);
00988             break;
00989         case 'F':  /* low water fd cache */
00990             high = atoi(opt->value);
00991             break;
00992         case 'w':  /* minimum server worker threads */
00993             workersMin = atoi(opt->value);
00994             break;
00995         case 'W':  /* maximum server worker threads */
00996             workersMax = atoi(opt->value);
00997             break;
00998         case 'e':  /* program execution time in seconds */
00999             execution = atoi(opt->value);
01000             break;
01001         case 's':  /* server's address */
01002             serverName = opt->value;
01003             break;
01004         case 'v':  /* verbosity */
01005             verbosity = IncrementVerbosity();
01006             break;
01007         case 'd':  /* debug mode */
01008             debug_mode = PR_TRUE;
01009             break;
01010         case 'p':  /* pthread mode */
01011             pthread_stats = PR_TRUE;
01012             break;
01013         case 'h':
01014         default:
01015             Help();
01016             return 2;
01017         }
01018     }
01019     PL_DestroyOptState(opt);
01020 
01021     if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) serverIsLocal = PR_FALSE;
01022     if (0 == execution) execution = DEFAULT_EXECUTION_TIME;
01023     if (0 == workersMax) workersMax = DEFAULT_WORKERS_MAX;
01024     if (0 == workersMin) workersMin = DEFAULT_WORKERS_MIN;
01025     if (0 == accepting) accepting = ALLOWED_IN_ACCEPT;
01026     if (0 == backlog) backlog = DEFAULT_BACKLOG;
01027 
01028     if (workersMin > accepting) accepting = workersMin;
01029 
01030     PR_STDIO_INIT();
01031     TimeOfDayMessage("Client/Server started at", PR_CurrentThread());
01032 
01033     cltsrv_log_file = PR_NewLogModule("cltsrv_log");
01034     MY_ASSERT(NULL != cltsrv_log_file);
01035     boolean = PR_SetLogFile("cltsrv.log");
01036     MY_ASSERT(boolean);
01037 
01038 #ifdef XP_MAC
01039     debug_mode = PR_TRUE;
01040 #endif
01041 
01042     rv = PR_SetFDCacheSize(low, high);
01043     PR_ASSERT(PR_SUCCESS == rv);
01044 
01045     if (serverIsLocal)
01046     {
01047         /* Establish the server */
01048         TEST_LOG(
01049             cltsrv_log_file, TEST_LOG_INFO,
01050             ("main(0x%p): starting server\n", PR_CurrentThread()));
01051 
01052         server = PR_NEWZAP(CSServer_t);
01053         PR_INIT_CLIST(&server->list);
01054         server->state = cs_init;
01055         server->ml = PR_NewLock();
01056         server->backlog = backlog;
01057         server->port = DEFAULT_PORT;
01058         server->workers.minimum = workersMin;
01059         server->workers.maximum = workersMax;
01060         server->workers.accepting = accepting;
01061         server->stateChange = PR_NewCondVar(server->ml);
01062         server->pool.exiting = PR_NewCondVar(server->ml);
01063         server->pool.acceptComplete = PR_NewCondVar(server->ml);
01064 
01065         TEST_LOG(
01066             cltsrv_log_file, TEST_LOG_NOTICE,
01067             ("main(0x%p): creating server thread\n", PR_CurrentThread()));
01068 
01069         server->thread = PR_CreateThread(
01070             PR_USER_THREAD, Server, server, PR_PRIORITY_HIGH,
01071             thread_scope, PR_JOINABLE_THREAD, 0);
01072         TEST_ASSERT(NULL != server->thread);
01073 
01074         TEST_LOG(
01075             cltsrv_log_file, TEST_LOG_VERBOSE,
01076             ("main(0x%p): waiting for server init\n", PR_CurrentThread()));
01077 
01078         PR_Lock(server->ml);
01079         while (server->state == cs_init)
01080             PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
01081         PR_Unlock(server->ml);
01082 
01083         TEST_LOG(
01084             cltsrv_log_file, TEST_LOG_VERBOSE,
01085             ("main(0x%p): server init complete (port #%d)\n",
01086             PR_CurrentThread(), server->port));
01087     }
01088 
01089     if (clients != 0)
01090     {
01091         /* Create all of the clients */
01092         PRHostEnt host;
01093         char buffer[BUFFER_SIZE];
01094         client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t));
01095 
01096         TEST_LOG(
01097             cltsrv_log_file, TEST_LOG_VERBOSE,
01098             ("main(0x%p): creating %d client threads\n",
01099             PR_CurrentThread(), clients));
01100         
01101         if (!serverIsLocal)
01102         {
01103             rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host);
01104             if (PR_SUCCESS != rv)
01105             {
01106                 PL_FPrintError(PR_STDERR, "PR_GetHostByName");
01107                 return 2;
01108             }
01109         }
01110 
01111         for (index = 0; index < clients; ++index)
01112         {
01113             client[index].state = cs_init;
01114             client[index].ml = PR_NewLock();
01115             if (serverIsLocal)
01116             {
01117                             if (PR_AF_INET6 != domain)
01118                      (void)PR_InitializeNetAddr(
01119                      PR_IpAddrLoopback, DEFAULT_PORT,
01120                      &client[index].serverAddress);
01121                             else
01122                                    rv = PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6,
01123                                                  DEFAULT_PORT, &client[index].serverAddress);
01124             }
01125             else
01126             {
01127                 (void)PR_EnumerateHostEnt(
01128                     0, &host, DEFAULT_PORT, &client[index].serverAddress);
01129             }
01130             client[index].stateChange = PR_NewCondVar(client[index].ml);
01131             TEST_LOG(
01132                 cltsrv_log_file, TEST_LOG_INFO,
01133                 ("main(0x%p): creating client threads\n", PR_CurrentThread()));
01134             client[index].thread = PR_CreateThread(
01135                 PR_USER_THREAD, Client, &client[index], PR_PRIORITY_NORMAL,
01136                 thread_scope, PR_JOINABLE_THREAD, 0);
01137             TEST_ASSERT(NULL != client[index].thread);
01138             PR_Lock(client[index].ml);
01139             while (cs_init == client[index].state)
01140                 PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
01141             PR_Unlock(client[index].ml);
01142         }
01143     }
01144 
01145     /* Then just let them go at it for a bit */
01146     TEST_LOG(
01147         cltsrv_log_file, TEST_LOG_ALWAYS,
01148         ("main(0x%p): waiting for execution interval (%d seconds)\n",
01149         PR_CurrentThread(), execution));
01150 
01151     WaitForCompletion(execution);
01152 
01153     TimeOfDayMessage("Shutting down", PR_CurrentThread());
01154 
01155     if (clients != 0)
01156     {
01157         for (index = 0; index < clients; ++index)
01158         {
01159             TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, 
01160                 ("main(0x%p): notifying client(0x%p) to stop\n",
01161                 PR_CurrentThread(), client[index].thread));
01162 
01163             PR_Lock(client[index].ml);
01164             if (cs_run == client[index].state)
01165             {
01166                 client[index].state = cs_stop;
01167                 PR_Interrupt(client[index].thread);
01168                 while (cs_stop == client[index].state)
01169                     PR_WaitCondVar(
01170                         client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
01171             }
01172             PR_Unlock(client[index].ml);
01173 
01174             TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE, 
01175                 ("main(0x%p): joining client(0x%p)\n",
01176                 PR_CurrentThread(), client[index].thread));
01177 
01178                   joinStatus = PR_JoinThread(client[index].thread);
01179                   TEST_ASSERT(PR_SUCCESS == joinStatus);
01180             PR_DestroyCondVar(client[index].stateChange);
01181             PR_DestroyLock(client[index].ml);
01182         }
01183         PR_DELETE(client);
01184     }
01185 
01186     if (NULL != server)
01187     {
01188         /* All clients joined - retrieve the server */
01189         TEST_LOG(
01190             cltsrv_log_file, TEST_LOG_NOTICE, 
01191             ("main(0x%p): notifying server(0x%p) to stop\n",
01192             PR_CurrentThread(), server->thread));
01193 
01194         PR_Lock(server->ml);
01195         server->state = cs_stop;
01196         PR_Interrupt(server->thread);
01197         while (cs_exit != server->state)
01198             PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
01199         PR_Unlock(server->ml);
01200 
01201         TEST_LOG(
01202             cltsrv_log_file, TEST_LOG_NOTICE, 
01203             ("main(0x%p): joining server(0x%p)\n",
01204             PR_CurrentThread(), server->thread));
01205         joinStatus = PR_JoinThread(server->thread);
01206         TEST_ASSERT(PR_SUCCESS == joinStatus);
01207 
01208         PR_DestroyCondVar(server->stateChange);
01209         PR_DestroyCondVar(server->pool.exiting);
01210         PR_DestroyCondVar(server->pool.acceptComplete);
01211         PR_DestroyLock(server->ml);
01212         PR_DELETE(server);
01213     }
01214 
01215     TEST_LOG(
01216         cltsrv_log_file, TEST_LOG_ALWAYS, 
01217         ("main(0x%p): test complete\n", PR_CurrentThread()));
01218 
01219     PT_FPrintStats(debug_out, "\nPThread Statistics\n");
01220 
01221     TimeOfDayMessage("Test exiting at", PR_CurrentThread());
01222     PR_Cleanup();
01223     return 0;
01224 }  /* main */
01225 
01226 /* cltsrv.c */