Back to index

lightning-sunbird  0.9+nobinonly
provider.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 BUFFER_SIZE 1024
00102 #define DEFAULT_BACKLOG 5
00103 #define DEFAULT_PORT 13000
00104 #define DEFAULT_CLIENTS 1
00105 #define ALLOWED_IN_ACCEPT 1
00106 #define DEFAULT_CLIPPING 1000
00107 #define DEFAULT_WORKERS_MIN 1
00108 #define DEFAULT_WORKERS_MAX 1
00109 #define DEFAULT_SERVER "localhost"
00110 #define DEFAULT_EXECUTION_TIME 10
00111 #define DEFAULT_CLIENT_TIMEOUT 4000
00112 #define DEFAULT_SERVER_TIMEOUT 4000
00113 #define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH
00114 
00115 typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t;
00116 
00117 static void PR_CALLBACK Worker(void *arg);
00118 typedef struct CSPool_s CSPool_t;
00119 typedef struct CSWorker_s CSWorker_t;
00120 typedef struct CSServer_s CSServer_t;
00121 typedef enum Verbosity
00122 {
00123     TEST_LOG_ALWAYS,
00124     TEST_LOG_ERROR,
00125     TEST_LOG_WARNING,
00126     TEST_LOG_NOTICE,
00127     TEST_LOG_INFO,
00128     TEST_LOG_STATUS,
00129     TEST_LOG_VERBOSE
00130 } Verbosity;
00131 
00132 static enum {
00133     thread_nspr, thread_pthread, thread_uithread, thread_sproc, thread_win32
00134 } thread_provider;
00135 
00136 static PRInt32 domain = AF_INET;
00137 static PRInt32 protocol = 6;  /* TCP */
00138 static PRFileDesc *debug_out = NULL;
00139 static PRBool debug_mode = PR_FALSE;
00140 static PRBool pthread_stats = PR_FALSE;
00141 static Verbosity verbosity = TEST_LOG_ALWAYS;
00142 static PRThreadScope thread_scope = PR_LOCAL_THREAD;
00143 
00144 struct CSWorker_s
00145 {
00146     PRCList element;        /* list of the server's workers */
00147 
00148     PRThread *thread;       /* this worker objects thread */
00149     CSServer_t *server;     /* back pointer to server structure */
00150 };
00151 
00152 struct CSPool_s
00153 {
00154     PRCondVar *exiting;
00155     PRCondVar *acceptComplete;
00156     PRUint32 accepting, active, workers;
00157 };
00158 
00159 struct CSServer_s
00160 {
00161     PRCList list;           /* head of worker list */
00162 
00163     PRLock *ml;
00164     PRThread *thread;       /* the main server thread */
00165     PRCondVar *stateChange;
00166 
00167     PRUint16 port;          /* port we're listening on */
00168     PRUint32 backlog;       /* size of our listener backlog */
00169     PRFileDesc *listener;   /* the fd accepting connections */
00170 
00171     CSPool_t pool;          /* statistics on worker threads */
00172     CSState_t state;        /* the server's state */
00173     struct                  /* controlling worker counts */
00174     {
00175         PRUint32 minimum, maximum, accepting;
00176     } workers;
00177 
00178     /* statistics */
00179     PRIntervalTime started, stopped;
00180     PRUint32 operations, bytesTransferred;
00181 };
00182 
00183 typedef struct CSDescriptor_s
00184 {
00185     PRInt32 size;       /* size of transfer */
00186     char filename[60];  /* filename, null padded */
00187 } CSDescriptor_t;
00188 
00189 typedef struct CSClient_s
00190 {
00191     PRLock *ml;
00192     PRThread *thread;
00193     PRCondVar *stateChange;
00194     PRNetAddr serverAddress;
00195 
00196     CSState_t state;
00197 
00198     /* statistics */
00199     PRIntervalTime started, stopped;
00200     PRUint32 operations, bytesTransferred;
00201 } CSClient_t;
00202 
00203 #define TEST_LOG(l, p, a) \
00204     do { \
00205         if (debug_mode || (p <= verbosity)) printf a; \
00206     } while (0)
00207 
00208 PRLogModuleInfo *cltsrv_log_file = NULL;
00209 
00210 #define MY_ASSERT(_expr) \
00211     ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
00212 
00213 #define TEST_ASSERT(_expr) \
00214     ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
00215 
00216 static void _MY_Assert(const char *s, const char *file, PRIntn ln)
00217 {
00218     PL_PrintError(NULL);
00219 #if DEBUG
00220     PR_Assert(s, file, ln);
00221 #endif
00222 }  /* _MW_Assert */
00223 
00224 static PRBool Aborted(PRStatus rv)
00225 {
00226     return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ?
00227         PR_TRUE : PR_FALSE;
00228 }
00229 
00230 static void TimeOfDayMessage(const char *msg, PRThread* me)
00231 {
00232     char buffer[100];
00233     PRExplodedTime tod;
00234     PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod);
00235     (void)PR_FormatTime(buffer, sizeof(buffer), "%T", &tod);
00236 
00237     TEST_LOG(
00238         cltsrv_log_file, TEST_LOG_ALWAYS,
00239         ("%s(0x%p): %s\n", msg, me, buffer));
00240 }  /* TimeOfDayMessage */
00241 
00242 
00243 static void PR_CALLBACK Client(void *arg)
00244 {
00245     PRStatus rv;
00246     PRIntn index;
00247     char buffer[1024];
00248     PRFileDesc *fd = NULL;
00249     PRUintn clipping = DEFAULT_CLIPPING;
00250     CSClient_t *client = (CSClient_t*)arg;
00251     PRThread *me = client->thread = PR_CurrentThread();
00252     CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
00253     PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT);
00254 
00255 
00256     for (index = 0; index < sizeof(buffer); ++index)
00257         buffer[index] = (char)index;
00258 
00259     client->started = PR_IntervalNow();
00260 
00261     PR_Lock(client->ml);
00262     client->state = cs_run;
00263     PR_NotifyCondVar(client->stateChange);
00264     PR_Unlock(client->ml);
00265 
00266     TimeOfDayMessage("Client started at", me);
00267 
00268     while (cs_run == client->state)
00269     {
00270         PRInt32 bytes, descbytes, filebytes, netbytes;
00271 
00272         (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer));
00273         TEST_LOG(cltsrv_log_file, TEST_LOG_INFO, 
00274             ("\tClient(0x%p): connecting to server at %s\n", me, buffer));
00275 
00276         fd = PR_Socket(domain, SOCK_STREAM, protocol);
00277         TEST_ASSERT(NULL != fd);
00278         rv = PR_Connect(fd, &client->serverAddress, timeout);
00279         if (PR_FAILURE == rv)
00280         {
00281             TEST_LOG(
00282                 cltsrv_log_file, TEST_LOG_ERROR,
00283                 ("\tClient(0x%p): conection failed\n", me));
00284             goto aborted;
00285         }
00286 
00287         memset(descriptor, 0, sizeof(*descriptor));
00288         descriptor->size = PR_htonl(descbytes = rand() % clipping);
00289         PR_snprintf(
00290             descriptor->filename, sizeof(descriptor->filename),
00291             "CS%p%p-%p.dat", client->started, me, client->operations);
00292         TEST_LOG(
00293             cltsrv_log_file, TEST_LOG_VERBOSE,
00294             ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes));
00295         bytes = PR_Send(
00296             fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout);
00297         if (sizeof(CSDescriptor_t) != bytes)
00298         {
00299             if (Aborted(PR_FAILURE)) goto aborted;
00300             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00301             {
00302                 TEST_LOG(
00303                     cltsrv_log_file, TEST_LOG_ERROR,
00304                     ("\tClient(0x%p): send descriptor timeout\n", me));
00305                 goto retry;
00306             }
00307         }
00308         TEST_ASSERT(sizeof(*descriptor) == bytes);
00309 
00310         netbytes = 0;
00311         while (netbytes < descbytes)
00312         {
00313             filebytes = sizeof(buffer);
00314             if ((descbytes - netbytes) < filebytes)
00315                 filebytes = descbytes - netbytes;
00316             TEST_LOG(
00317                 cltsrv_log_file, TEST_LOG_VERBOSE,
00318                 ("\tClient(0x%p): sending %d bytes\n", me, filebytes));
00319             bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
00320             if (filebytes != bytes)
00321             {
00322                 if (Aborted(PR_FAILURE)) goto aborted;
00323                 if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00324                 {
00325                     TEST_LOG(
00326                         cltsrv_log_file, TEST_LOG_ERROR,
00327                         ("\tClient(0x%p): send data timeout\n", me));
00328                     goto retry;
00329                 }
00330             }
00331             TEST_ASSERT(bytes == filebytes);
00332             netbytes += bytes;
00333         }
00334         filebytes = 0;
00335         while (filebytes < descbytes)
00336         {
00337             netbytes = sizeof(buffer);
00338             if ((descbytes - filebytes) < netbytes)
00339                 netbytes = descbytes - filebytes;
00340             TEST_LOG(
00341                 cltsrv_log_file, TEST_LOG_VERBOSE,
00342                 ("\tClient(0x%p): receiving %d bytes\n", me, netbytes));
00343             bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
00344             if (-1 == bytes)
00345             {
00346                 if (Aborted(PR_FAILURE))
00347                 {
00348                     TEST_LOG(
00349                         cltsrv_log_file, TEST_LOG_ERROR,
00350                         ("\tClient(0x%p): receive data aborted\n", me));
00351                     goto aborted;
00352                 }
00353                 else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00354                     TEST_LOG(
00355                         cltsrv_log_file, TEST_LOG_ERROR,
00356                         ("\tClient(0x%p): receive data timeout\n", me));
00357                             else
00358                     TEST_LOG(
00359                         cltsrv_log_file, TEST_LOG_ERROR,
00360                         ("\tClient(0x%p): receive error (%d, %d)\n",
00361                                           me, PR_GetError(), PR_GetOSError()));
00362                 goto retry;
00363            }
00364             if (0 == bytes)
00365             {
00366                 TEST_LOG(
00367                     cltsrv_log_file, TEST_LOG_ERROR,
00368                     ("\t\tClient(0x%p): unexpected end of stream\n",
00369                     PR_CurrentThread()));
00370                 break;
00371             }
00372             filebytes += bytes;
00373         }
00374 
00375         rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
00376         if (Aborted(rv)) goto aborted;
00377         TEST_ASSERT(PR_SUCCESS == rv);
00378 retry:
00379         (void)PR_Close(fd); fd = NULL;
00380         TEST_LOG(
00381             cltsrv_log_file, TEST_LOG_INFO,
00382             ("\tClient(0x%p): disconnected from server\n", me));
00383 
00384         PR_Lock(client->ml);
00385         client->operations += 1;
00386         client->bytesTransferred += 2 * descbytes;
00387         rv = PR_WaitCondVar(client->stateChange, rand() % clipping);
00388         PR_Unlock(client->ml);
00389         if (Aborted(rv)) break;
00390     }
00391 
00392 aborted:
00393     client->stopped = PR_IntervalNow();
00394 
00395     PR_ClearInterrupt();
00396     if (NULL != fd) rv = PR_Close(fd);
00397 
00398     PR_Lock(client->ml);
00399     client->state = cs_exit;
00400     PR_NotifyCondVar(client->stateChange);
00401     PR_Unlock(client->ml);
00402     PR_DELETE(descriptor);
00403     TEST_LOG(
00404         cltsrv_log_file, TEST_LOG_ALWAYS,
00405         ("\tClient(0x%p): stopped after %u operations and %u bytes\n",
00406         PR_CurrentThread(), client->operations, client->bytesTransferred));
00407 
00408 }  /* Client */
00409 
00410 static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server)
00411 {
00412     PRStatus drv, rv;
00413     char buffer[1024];
00414     PRFileDesc *file = NULL;
00415     PRThread * me = PR_CurrentThread();
00416     PRInt32 bytes, descbytes, netbytes, filebytes = 0;
00417     CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
00418     PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT);
00419 
00420     TEST_LOG(
00421         cltsrv_log_file, TEST_LOG_VERBOSE,
00422         ("\tProcessRequest(0x%p): receiving desciptor\n", me));
00423     bytes = PR_Recv(
00424         fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout);
00425     if (-1 == bytes)
00426     {
00427         rv = PR_FAILURE;
00428         if (Aborted(rv)) goto exit;
00429         if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00430         {
00431             TEST_LOG(
00432                 cltsrv_log_file, TEST_LOG_ERROR,
00433                 ("\tProcessRequest(0x%p): receive timeout\n", me));
00434         }
00435         goto exit;
00436     }
00437     if (0 == bytes)
00438     {
00439         rv = PR_FAILURE;
00440         TEST_LOG(
00441             cltsrv_log_file, TEST_LOG_ERROR,
00442             ("\tProcessRequest(0x%p): unexpected end of file\n", me));
00443         goto exit;
00444     }
00445     descbytes = PR_ntohl(descriptor->size);
00446     TEST_ASSERT(sizeof(*descriptor) == bytes);
00447 
00448     TEST_LOG(
00449         cltsrv_log_file, TEST_LOG_VERBOSE, 
00450         ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n",
00451         me, descbytes, descriptor->filename));
00452 
00453     file = PR_Open(
00454         descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666);
00455     if (NULL == file)
00456     {
00457         rv = PR_FAILURE;
00458         if (Aborted(rv)) goto aborted;
00459         if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00460         {
00461             TEST_LOG(
00462                 cltsrv_log_file, TEST_LOG_ERROR,
00463                 ("\tProcessRequest(0x%p): open file timeout\n", me));
00464             goto aborted;
00465         }
00466     }
00467     TEST_ASSERT(NULL != file);
00468 
00469     filebytes = 0;
00470     while (filebytes < descbytes)
00471     {
00472         netbytes = sizeof(buffer);
00473         if ((descbytes - filebytes) < netbytes)
00474             netbytes = descbytes - filebytes;
00475         TEST_LOG(
00476             cltsrv_log_file, TEST_LOG_VERBOSE,
00477             ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes));
00478         bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
00479         if (-1 == bytes)
00480         {
00481             rv = PR_FAILURE;
00482             if (Aborted(rv)) goto aborted;
00483             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00484             {
00485                 TEST_LOG(
00486                     cltsrv_log_file, TEST_LOG_ERROR,
00487                     ("\t\tProcessRequest(0x%p): receive data timeout\n", me));
00488                 goto aborted;
00489             }
00490             /*
00491              * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
00492              * on NT here.  This is equivalent to ECONNRESET on Unix.
00493              *     -wtc
00494              */
00495             TEST_LOG(
00496                 cltsrv_log_file, TEST_LOG_WARNING,
00497                 ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n",
00498                 me, PR_GetError(), PR_GetOSError()));
00499             goto aborted;
00500         }
00501         if(0 == bytes)
00502         {
00503             TEST_LOG(
00504                 cltsrv_log_file, TEST_LOG_WARNING,
00505                 ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
00506             rv = PR_FAILURE;
00507             goto aborted;
00508         }
00509         filebytes += bytes;
00510         netbytes = bytes;
00511         /* The byte count for PR_Write should be positive */
00512         MY_ASSERT(netbytes > 0);
00513         TEST_LOG(
00514             cltsrv_log_file, TEST_LOG_VERBOSE,
00515             ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes));
00516         bytes = PR_Write(file, buffer, netbytes);
00517         if (netbytes != bytes)
00518         {
00519             rv = PR_FAILURE;
00520             if (Aborted(rv)) goto aborted;
00521             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
00522             {
00523                 TEST_LOG(
00524                     cltsrv_log_file, TEST_LOG_ERROR,
00525                     ("\t\tProcessRequest(0x%p): write file timeout\n", me));
00526                 goto aborted;
00527             }
00528         }
00529         TEST_ASSERT(bytes > 0);
00530     }
00531 
00532     PR_Lock(server->ml);
00533     server->operations += 1;
00534     server->bytesTransferred += filebytes;
00535     PR_Unlock(server->ml);
00536 
00537     rv = PR_Close(file); file = NULL;
00538     if (Aborted(rv)) goto aborted;
00539     TEST_ASSERT(PR_SUCCESS == rv);
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); file = NULL;
00621     if (Aborted(rv)) goto aborted;
00622     TEST_ASSERT(PR_SUCCESS == rv);
00623 
00624 aborted:
00625     PR_ClearInterrupt();
00626     if (NULL != file) PR_Close(file);
00627     drv = PR_Delete(descriptor->filename);
00628     TEST_ASSERT(PR_SUCCESS == drv);
00629 exit:
00630     TEST_LOG(
00631         cltsrv_log_file, TEST_LOG_VERBOSE,
00632         ("\t\tProcessRequest(0x%p): Finished\n", me));
00633 
00634     PR_DELETE(descriptor);
00635 
00636 #if defined(WIN95)
00637     PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */
00638 #endif
00639     return rv;
00640 }  /* ProcessRequest */
00641 
00642 typedef void (*StartFn)(void*);
00643 typedef struct StartObject
00644 {
00645     StartFn start;
00646     void *arg;
00647 } StartObject;
00648 
00649 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
00650 #include "md/_pth.h"
00651 #include <pthread.h>
00652 
00653 static void *pthread_start(void *arg)
00654 {
00655     StartObject *so = (StartObject*)arg;
00656     StartFn start = so->start;
00657     void *data = so->arg;
00658     PR_Free(so);
00659     start(data);
00660     return NULL;
00661 }  /* pthread_start */
00662 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
00663 
00664 #if defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
00665 #include <thread.h>
00666 
00667 static void *uithread_start(void *arg)
00668 {
00669     StartObject *so = (StartObject*)arg;
00670     StartFn start = so->start;
00671     void *data = so->arg;
00672     PR_Free(so);
00673     start(data);
00674     return NULL;
00675 }  /* uithread_start */
00676 #endif /* defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY) */
00677 
00678 #if defined(IRIX) && !defined(_PR_PTHREADS)
00679 #include <sys/types.h>
00680 #include <sys/prctl.h>
00681 static void sproc_start(void *arg, PRSize size)
00682 {
00683     StartObject *so = (StartObject*)arg;
00684     StartFn start = so->start;
00685     void *data = so->arg;
00686     PR_Free(so);
00687     start(data);
00688 }  /* sproc_start */
00689 #endif  /* defined(IRIX) && !defined(_PR_PTHREADS) */
00690 
00691 #if defined(WIN32)
00692 #include <process.h>  /* for _beginthreadex() */
00693 
00694 static PRUintn __stdcall windows_start(void *arg)
00695 {
00696     StartObject *so = (StartObject*)arg;
00697     StartFn start = so->start;
00698     void *data = so->arg;
00699     PR_Free(so);
00700     start(data);
00701     return 0;
00702 }  /* windows_start */
00703 #endif /* defined(WIN32) */
00704 
00705 static PRStatus JoinThread(PRThread *thread)
00706 {
00707     PRStatus rv;
00708     switch (thread_provider)
00709     {
00710     case thread_nspr:
00711         rv = PR_JoinThread(thread);
00712         break;
00713     case thread_pthread:
00714 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
00715         rv = PR_SUCCESS;
00716         break;
00717 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
00718     case thread_uithread:
00719 #if defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
00720         rv = PR_SUCCESS;
00721         break;
00722 #endif /* defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY) */
00723     case thread_win32:
00724 #if defined(WIN32)
00725         rv = PR_SUCCESS;
00726         break;
00727 #endif
00728     default:
00729         rv = PR_FAILURE;
00730         break;
00731     }
00732     return rv;    
00733 }  /* JoinThread */
00734 
00735 static PRStatus NewThread(
00736     StartFn start, void *arg, PRThreadPriority prio, PRThreadState state)
00737 {
00738     PRStatus rv;
00739 
00740     switch (thread_provider)
00741     {
00742     case thread_nspr:
00743         {
00744             PRThread *thread = PR_CreateThread(
00745                 PR_USER_THREAD, start, arg,
00746                 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
00747                 PR_JOINABLE_THREAD, 0);
00748             rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS;
00749         }
00750         break;
00751     case thread_pthread:
00752 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
00753         {
00754             int rv;
00755             pthread_t id;
00756             pthread_attr_t tattr;
00757             StartObject *start_object;
00758             start_object = PR_NEW(StartObject);
00759             PR_ASSERT(NULL != start_object);
00760             start_object->start = start;
00761             start_object->arg = arg;
00762 
00763             rv = _PT_PTHREAD_ATTR_INIT(&tattr);
00764             PR_ASSERT(0 == rv);
00765 
00766             rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
00767             PR_ASSERT(0 == rv);
00768 
00769             rv = pthread_attr_setstacksize(&tattr, 64 * 1024);
00770             PR_ASSERT(0 == rv);
00771 
00772             rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object);
00773             (void)_PT_PTHREAD_ATTR_DESTROY(&tattr);
00774             return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
00775         }
00776 #else
00777         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
00778         rv = PR_FAILURE;
00779 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
00780         break;
00781 
00782     case thread_uithread:
00783 #if defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
00784         {
00785             int rv;
00786             thread_t id;
00787             long flags;
00788             StartObject *start_object;
00789             start_object = PR_NEW(StartObject);
00790             PR_ASSERT(NULL != start_object);
00791             start_object->start = start;
00792             start_object->arg = arg;
00793 
00794             flags = THR_DETACHED;
00795 
00796             rv = thr_create(NULL, NULL, uithread_start, start_object, flags, &id);
00797             return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
00798         }
00799 #else
00800         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
00801         rv = PR_FAILURE;
00802 #endif /* defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY) */
00803         break;
00804 
00805     case thread_sproc:
00806 #if defined(IRIX) && !defined(_PR_PTHREADS)
00807         {
00808             PRInt32 pid;
00809             StartObject *start_object;
00810             start_object = PR_NEW(StartObject);
00811             PR_ASSERT(NULL != start_object);
00812             start_object->start = start;
00813             start_object->arg = arg;
00814             pid = sprocsp(
00815                 sproc_start, PR_SALL, start_object, NULL, 64 * 1024);
00816             rv = (0 < pid) ? PR_SUCCESS : PR_FAILURE;
00817         }
00818 #else
00819         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
00820         rv = PR_FAILURE;
00821 #endif  /* defined(IRIX) && !defined(_PR_PTHREADS) */
00822         break;
00823     case thread_win32:
00824 #if defined(WIN32)
00825         {
00826             void *th;
00827             PRUintn id;       
00828             StartObject *start_object;
00829             start_object = PR_NEW(StartObject);
00830             PR_ASSERT(NULL != start_object);
00831             start_object->start = start;
00832             start_object->arg = arg;
00833             th = (void*)_beginthreadex(
00834                 NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */  
00835                 0U, /* DWORD - initial thread stack size, in bytes */
00836                 windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */
00837                 start_object, /* LPVOID - argument for new thread */
00838                 0U, /*DWORD dwCreationFlags - creation flags */
00839                 &id /* LPDWORD - pointer to returned thread identifier */ );
00840 
00841             rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS;
00842         }
00843 #else
00844         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
00845         rv = PR_FAILURE;
00846 #endif
00847         break;
00848     default:
00849         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
00850         rv = PR_FAILURE;
00851     }
00852     return rv;
00853 }  /* NewThread */
00854 
00855 static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool)
00856 {
00857     PRStatus rv;
00858     CSWorker_t *worker = PR_NEWZAP(CSWorker_t);
00859     worker->server = server;
00860     PR_INIT_CLIST(&worker->element);
00861     rv = NewThread(
00862         Worker, worker, DEFAULT_SERVER_PRIORITY, PR_UNJOINABLE_THREAD);
00863     if (PR_FAILURE == rv) PR_DELETE(worker);
00864 
00865     TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, 
00866         ("\tCreateWorker(0x%p): create new worker (0x%p)\n",
00867         PR_CurrentThread(), worker->thread));
00868 
00869     return rv;
00870 }  /* CreateWorker */
00871 
00872 static void PR_CALLBACK Worker(void *arg)
00873 {
00874     PRStatus rv;
00875     PRNetAddr from;
00876     PRFileDesc *fd = NULL;
00877     CSWorker_t *worker = (CSWorker_t*)arg;
00878     CSServer_t *server = worker->server;
00879     CSPool_t *pool = &server->pool;
00880 
00881     PRThread *me = worker->thread = PR_CurrentThread();
00882 
00883     TEST_LOG(
00884         cltsrv_log_file, TEST_LOG_NOTICE,
00885         ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1));
00886 
00887     PR_Lock(server->ml);
00888     PR_APPEND_LINK(&worker->element, &server->list);
00889     pool->workers += 1;  /* define our existance */
00890 
00891     while (cs_run == server->state)
00892     {
00893         while (pool->accepting >= server->workers.accepting)
00894         {
00895             TEST_LOG(
00896                 cltsrv_log_file, TEST_LOG_VERBOSE,
00897                 ("\t\tWorker(0x%p): waiting for accept slot[%d]\n",
00898                 me, pool->accepting));
00899             rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT);
00900             if (Aborted(rv) || (cs_run != server->state))
00901             {
00902                 TEST_LOG(
00903                     cltsrv_log_file, TEST_LOG_NOTICE,
00904                     ("\tWorker(0x%p): has been %s\n",
00905                     me, (Aborted(rv) ? "interrupted" : "stopped")));
00906                 goto exit;
00907             }
00908         } 
00909         pool->accepting += 1;  /* how many are really in accept */
00910         PR_Unlock(server->ml);
00911 
00912         TEST_LOG(
00913             cltsrv_log_file, TEST_LOG_VERBOSE,
00914             ("\t\tWorker(0x%p): calling accept\n", me));
00915         fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT);
00916 
00917         PR_Lock(server->ml);        
00918         pool->accepting -= 1;
00919         PR_NotifyCondVar(pool->acceptComplete);
00920 
00921         if ((NULL == fd) && Aborted(PR_FAILURE))
00922         {
00923             if (NULL != server->listener)
00924             {
00925                 PR_Close(server->listener);
00926                 server->listener = NULL;
00927             }
00928             goto exit;
00929         }
00930 
00931         if (NULL != fd)
00932         {
00933             /*
00934             ** Create another worker of the total number of workers is
00935             ** less than the minimum specified or we have none left in
00936             ** accept() AND we're not over the maximum.
00937             ** This sort of presumes that the number allowed in accept
00938             ** is at least as many as the minimum. Otherwise we'll keep
00939             ** creating new threads and deleting them soon after.
00940             */
00941             PRBool another =
00942                 ((pool->workers < server->workers.minimum) ||
00943                 ((0 == pool->accepting)
00944                     && (pool->workers < server->workers.maximum))) ?
00945                     PR_TRUE : PR_FALSE;
00946             pool->active += 1;
00947             PR_Unlock(server->ml);
00948 
00949             if (another) (void)CreateWorker(server, pool);
00950 
00951             rv = ProcessRequest(fd, server);
00952             if (PR_SUCCESS != rv)
00953                 TEST_LOG(
00954                     cltsrv_log_file, TEST_LOG_ERROR,
00955                     ("\t\tWorker(0x%p): server process ended abnormally\n", me));
00956             (void)PR_Close(fd); fd = NULL;
00957 
00958             PR_Lock(server->ml);
00959             pool->active -= 1;
00960         }
00961     }
00962 
00963 exit:
00964     PR_ClearInterrupt();    
00965     PR_Unlock(server->ml);
00966 
00967     if (NULL != fd)
00968     {
00969         (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
00970         (void)PR_Close(fd);
00971     }
00972 
00973     TEST_LOG(
00974         cltsrv_log_file, TEST_LOG_NOTICE,
00975         ("\t\tWorker(0x%p): exiting [%u]\n", PR_CurrentThread(), pool->workers));
00976 
00977     PR_Lock(server->ml);
00978     pool->workers -= 1;  /* undefine our existance */
00979     PR_REMOVE_AND_INIT_LINK(&worker->element);
00980     PR_NotifyCondVar(pool->exiting);
00981     PR_Unlock(server->ml);
00982 
00983     PR_DELETE(worker);  /* destruction of the "worker" object */
00984 
00985 }  /* Worker */
00986 
00987 static void PR_CALLBACK Server(void *arg)
00988 {
00989     PRStatus rv;
00990     PRNetAddr serverAddress;
00991     CSServer_t *server = (CSServer_t*)arg;
00992     PRThread *me = server->thread = PR_CurrentThread();
00993     PRSocketOptionData sockOpt;
00994 
00995     server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
00996 
00997     sockOpt.option = PR_SockOpt_Reuseaddr;
00998     sockOpt.value.reuse_addr = PR_TRUE;
00999     rv = PR_SetSocketOption(server->listener, &sockOpt);
01000     TEST_ASSERT(PR_SUCCESS == rv);
01001 
01002     memset(&serverAddress, 0, sizeof(serverAddress));
01003     rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
01004 
01005     rv = PR_Bind(server->listener, &serverAddress);
01006     TEST_ASSERT(PR_SUCCESS == rv);
01007 
01008     rv = PR_Listen(server->listener, server->backlog);
01009     TEST_ASSERT(PR_SUCCESS == rv);
01010 
01011     server->started = PR_IntervalNow();
01012     TimeOfDayMessage("Server started at", me);
01013 
01014     PR_Lock(server->ml);
01015     server->state = cs_run;
01016     PR_NotifyCondVar(server->stateChange);
01017     PR_Unlock(server->ml);
01018 
01019     /*
01020     ** Create the first worker (actually, a thread that accepts
01021     ** connections and then processes the work load as needed).
01022     ** From this point on, additional worker threads are created
01023     ** as they are needed by existing worker threads.
01024     */
01025     rv = CreateWorker(server, &server->pool);
01026     TEST_ASSERT(PR_SUCCESS == rv);
01027 
01028     /*
01029     ** From here on this thread is merely hanging around as the contact
01030     ** point for the main test driver. It's just waiting for the driver
01031     ** to declare the test complete.
01032     */
01033     TEST_LOG(
01034         cltsrv_log_file, TEST_LOG_VERBOSE,
01035         ("\tServer(0x%p): waiting for state change\n", me));
01036 
01037     PR_Lock(server->ml);
01038     while ((cs_run == server->state) && !Aborted(rv))
01039     {
01040         rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
01041     }
01042     PR_Unlock(server->ml);
01043     PR_ClearInterrupt();
01044 
01045     TEST_LOG(
01046         cltsrv_log_file, TEST_LOG_INFO,
01047         ("\tServer(0x%p): shutting down workers\n", me));
01048 
01049     /*
01050     ** Get all the worker threads to exit. They know how to
01051     ** clean up after themselves, so this is just a matter of
01052     ** waiting for clorine in the pool to take effect. During
01053     ** this stage we're ignoring interrupts.
01054     */
01055     server->workers.minimum = server->workers.maximum = 0;
01056 
01057     PR_Lock(server->ml);
01058     while (!PR_CLIST_IS_EMPTY(&server->list))
01059     {
01060         PRCList *head = PR_LIST_HEAD(&server->list);
01061         CSWorker_t *worker = (CSWorker_t*)head;
01062         TEST_LOG(
01063             cltsrv_log_file, TEST_LOG_VERBOSE,
01064             ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker));
01065         rv = PR_Interrupt(worker->thread);
01066         TEST_ASSERT(PR_SUCCESS == rv);
01067         PR_REMOVE_AND_INIT_LINK(head);
01068     }
01069 
01070     while (server->pool.workers > 0)
01071     {
01072         TEST_LOG(
01073             cltsrv_log_file, TEST_LOG_NOTICE,
01074             ("\tServer(0x%p): waiting for %u workers to exit\n",
01075             me, server->pool.workers));
01076         (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT);
01077     }
01078 
01079     server->state = cs_exit;
01080     PR_NotifyCondVar(server->stateChange);
01081     PR_Unlock(server->ml);
01082 
01083     TEST_LOG(
01084         cltsrv_log_file, TEST_LOG_ALWAYS,
01085         ("\tServer(0x%p): stopped after %u operations and %u bytes\n",
01086         me, server->operations, server->bytesTransferred));
01087 
01088     if (NULL != server->listener) PR_Close(server->listener);
01089     server->stopped = PR_IntervalNow();
01090 
01091 }  /* Server */
01092 
01093 static void WaitForCompletion(PRIntn execution)
01094 {
01095     while (execution > 0)
01096     { 
01097         PRIntn dally = (execution > 30) ? 30 : execution;
01098         PR_Sleep(PR_SecondsToInterval(dally));
01099         if (pthread_stats) PT_FPrintStats(debug_out, "\nPThread Statistics\n");
01100         execution -= dally;
01101     }
01102 }  /* WaitForCompletion */
01103 
01104 static void Help(void)
01105 {
01106     PR_fprintf(debug_out, "cltsrv test program usage:\n");
01107     PR_fprintf(debug_out, "\t-a <n>       threads allowed in accept        (5)\n");
01108     PR_fprintf(debug_out, "\t-b <n>       backlock for listen              (5)\n");
01109     PR_fprintf(debug_out, "\t-c <threads> number of clients to create      (1)\n");
01110     PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n");
01111     PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n");
01112     PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds  (10)\n");
01113     PR_fprintf(debug_out, "\t-s <string>  dsn name of server               (localhost)\n");
01114     PR_fprintf(debug_out, "\t-G           use GLOBAL threads               (LOCAL)\n");
01115     PR_fprintf(debug_out, "\t-T <string>  thread provider ('n' | 'p' | 'u' | 'w')(n)\n");
01116     PR_fprintf(debug_out, "\t-X           use XTP as transport             (TCP)\n");
01117     PR_fprintf(debug_out, "\t-6           Use IPv6                         (IPv4)\n");
01118     PR_fprintf(debug_out, "\t-v           verbosity (accumulative)         (0)\n");
01119     PR_fprintf(debug_out, "\t-p           pthread statistics               (FALSE)\n");
01120     PR_fprintf(debug_out, "\t-d           debug mode                       (FALSE)\n");
01121     PR_fprintf(debug_out, "\t-h           this message\n");
01122 }  /* Help */
01123 
01124 static Verbosity IncrementVerbosity(void)
01125 {
01126     PRIntn verboge = (PRIntn)verbosity + 1;
01127     return (Verbosity)verboge;
01128 }  /* IncrementVerbosity */
01129 
01130 PRIntn main(PRIntn argc, char** argv)
01131 {
01132     PRUintn index;
01133     PRBool boolean;
01134     CSClient_t *client;
01135     PRStatus rv, joinStatus;
01136     CSServer_t *server = NULL;
01137        char *thread_type;
01138 
01139     PRUintn backlog = DEFAULT_BACKLOG;
01140     PRUintn clients = DEFAULT_CLIENTS;
01141     const char *serverName = DEFAULT_SERVER;
01142     PRBool serverIsLocal = PR_TRUE;
01143     PRUintn accepting = ALLOWED_IN_ACCEPT;
01144     PRUintn workersMin = DEFAULT_WORKERS_MIN;
01145     PRUintn workersMax = DEFAULT_WORKERS_MAX;
01146     PRIntn execution = DEFAULT_EXECUTION_TIME;
01147 
01148     /*
01149      * -G           use global threads
01150      * -a <n>       threads allowed in accept
01151      * -b <n>       backlock for listen
01152      * -c <threads> number of clients to create
01153      * -w <threads> minimal number of server threads
01154      * -W <threads> maximum number of server threads
01155      * -e <seconds> duration of the test in seconds
01156      * -s <string>  dsn name of server (implies no server here)
01157      * -v           verbosity
01158      */
01159 
01160     PLOptStatus os;
01161     PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:w:W:e:s:T:vdhp");
01162 
01163 #if defined(WIN32)
01164        thread_provider = thread_win32;
01165 #elif defined(_PR_PTHREADS)
01166        thread_provider = thread_pthread;
01167 #elif defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
01168        thread_provider = thread_uithread;
01169 #elif defined(IRIX)
01170        thread_provider = thread_sproc;
01171 #else
01172     thread_provider = thread_nspr;
01173 #endif
01174 
01175     debug_out = PR_GetSpecialFD(PR_StandardError);
01176 
01177     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
01178     {
01179         if (PL_OPT_BAD == os) continue;
01180         switch (opt->option)
01181         {
01182         case 'G':  /* use global threads */
01183             thread_scope = PR_GLOBAL_THREAD;
01184             break;
01185         case 'X':  /* use XTP as transport */
01186             protocol = 36;
01187             break;
01188               case '6':  /* Use IPv6 */
01189             domain = PR_AF_INET6;
01190             break;
01191         case 'a':  /* the value for accepting */
01192             accepting = atoi(opt->value);
01193             break;
01194         case 'b':  /* the value for backlock */
01195             backlog = atoi(opt->value);
01196             break;
01197         case 'T':  /* the thread provider */
01198             if ('n' == *opt->value) thread_provider = thread_nspr;
01199             else if ('p' == *opt->value) thread_provider = thread_pthread;
01200             else if ('u' == *opt->value) thread_provider = thread_uithread;
01201             else if ('w' == *opt->value) thread_provider = thread_win32;
01202             else {Help(); return 2; }
01203             break;
01204         case 'c':  /* number of client threads */
01205             clients = atoi(opt->value);
01206             break;
01207         case 'w':  /* minimum server worker threads */
01208             workersMin = atoi(opt->value);
01209             break;
01210         case 'W':  /* maximum server worker threads */
01211             workersMax = atoi(opt->value);
01212             break;
01213         case 'e':  /* program execution time in seconds */
01214             execution = atoi(opt->value);
01215             break;
01216         case 's':  /* server's address */
01217             serverName = opt->value;
01218             break;
01219         case 'v':  /* verbosity */
01220             verbosity = IncrementVerbosity();
01221             break;
01222         case 'd':  /* debug mode */
01223             debug_mode = PR_TRUE;
01224             break;
01225         case 'p':  /* pthread mode */
01226             pthread_stats = PR_TRUE;
01227             break;
01228         case 'h':
01229         default:
01230             Help();
01231             return 2;
01232         }
01233     }
01234     PL_DestroyOptState(opt);
01235 
01236     if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) serverIsLocal = PR_FALSE;
01237     if (0 == execution) execution = DEFAULT_EXECUTION_TIME;
01238     if (0 == workersMax) workersMax = DEFAULT_WORKERS_MAX;
01239     if (0 == workersMin) workersMin = DEFAULT_WORKERS_MIN;
01240     if (0 == accepting) accepting = ALLOWED_IN_ACCEPT;
01241     if (0 == backlog) backlog = DEFAULT_BACKLOG;
01242 
01243     if (workersMin > accepting) accepting = workersMin;
01244 
01245     PR_STDIO_INIT();
01246     TimeOfDayMessage("Client/Server started at", PR_CurrentThread());
01247 
01248     cltsrv_log_file = PR_NewLogModule("cltsrv_log");
01249     MY_ASSERT(NULL != cltsrv_log_file);
01250     boolean = PR_SetLogFile("cltsrv.log");
01251     MY_ASSERT(boolean);
01252 
01253 #ifdef XP_MAC
01254     debug_mode = PR_TRUE;
01255 #endif
01256 
01257     if (serverIsLocal)
01258     {
01259         /* Establish the server */
01260         TEST_LOG(
01261             cltsrv_log_file, TEST_LOG_INFO,
01262             ("main(0x%p): starting server\n", PR_CurrentThread()));
01263 
01264         server = PR_NEWZAP(CSServer_t);
01265         PR_INIT_CLIST(&server->list);
01266         server->state = cs_init;
01267         server->ml = PR_NewLock();
01268         server->backlog = backlog;
01269         server->port = DEFAULT_PORT;
01270         server->workers.minimum = workersMin;
01271         server->workers.maximum = workersMax;
01272         server->workers.accepting = accepting;
01273         server->stateChange = PR_NewCondVar(server->ml);
01274         server->pool.exiting = PR_NewCondVar(server->ml);
01275         server->pool.acceptComplete = PR_NewCondVar(server->ml);
01276 
01277         TEST_LOG(
01278             cltsrv_log_file, TEST_LOG_NOTICE,
01279             ("main(0x%p): creating server thread\n", PR_CurrentThread()));
01280 
01281         rv = NewThread(
01282             Server, server, PR_PRIORITY_HIGH, PR_JOINABLE_THREAD);
01283         TEST_ASSERT(PR_SUCCESS == rv);
01284 
01285         TEST_LOG(
01286             cltsrv_log_file, TEST_LOG_VERBOSE,
01287             ("main(0x%p): waiting for server init\n", PR_CurrentThread()));
01288 
01289         PR_Lock(server->ml);
01290         while (server->state == cs_init)
01291             PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
01292         PR_Unlock(server->ml);
01293 
01294         TEST_LOG(
01295             cltsrv_log_file, TEST_LOG_VERBOSE,
01296             ("main(0x%p): server init complete (port #%d)\n",
01297             PR_CurrentThread(), server->port));
01298     }
01299 
01300     if (clients != 0)
01301     {
01302         /* Create all of the clients */
01303         PRHostEnt host;
01304         char buffer[BUFFER_SIZE];
01305         client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t));
01306 
01307         TEST_LOG(
01308             cltsrv_log_file, TEST_LOG_VERBOSE,
01309             ("main(0x%p): creating %d client threads\n",
01310             PR_CurrentThread(), clients));
01311         
01312         if (!serverIsLocal)
01313         {
01314             rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host);
01315             if (PR_SUCCESS != rv)
01316             {
01317                 PL_FPrintError(PR_STDERR, "PR_GetHostByName");
01318                 return 2;
01319             }
01320         }
01321 
01322         for (index = 0; index < clients; ++index)
01323         {
01324             client[index].state = cs_init;
01325             client[index].ml = PR_NewLock();
01326             if (serverIsLocal)
01327             {
01328                 (void)PR_InitializeNetAddr(
01329                     PR_IpAddrLoopback, DEFAULT_PORT,
01330                     &client[index].serverAddress);
01331             }
01332             else
01333             {
01334                 (void)PR_EnumerateHostEnt(
01335                     0, &host, DEFAULT_PORT, &client[index].serverAddress);
01336             }
01337             client[index].stateChange = PR_NewCondVar(client[index].ml);
01338             TEST_LOG(
01339                 cltsrv_log_file, TEST_LOG_INFO,
01340                 ("main(0x%p): creating client threads\n", PR_CurrentThread()));
01341             rv = NewThread(
01342                 Client, &client[index], PR_PRIORITY_NORMAL, PR_JOINABLE_THREAD);
01343             TEST_ASSERT(PR_SUCCESS == rv);
01344             PR_Lock(client[index].ml);
01345             while (cs_init == client[index].state)
01346                 PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
01347             PR_Unlock(client[index].ml);
01348         }
01349     }
01350 
01351     /* Then just let them go at it for a bit */
01352     TEST_LOG(
01353         cltsrv_log_file, TEST_LOG_ALWAYS,
01354         ("main(0x%p): waiting for execution interval (%d seconds)\n",
01355         PR_CurrentThread(), execution));
01356 
01357     WaitForCompletion(execution);
01358 
01359     TimeOfDayMessage("Shutting down", PR_CurrentThread());
01360 
01361     if (clients != 0)
01362     {
01363         for (index = 0; index < clients; ++index)
01364         {
01365             TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, 
01366                 ("main(0x%p): notifying client(0x%p) to stop\n",
01367                 PR_CurrentThread(), client[index].thread));
01368 
01369             PR_Lock(client[index].ml);
01370             if (cs_run == client[index].state)
01371             {
01372                 client[index].state = cs_stop;
01373                 PR_Interrupt(client[index].thread);
01374                 while (cs_stop == client[index].state)
01375                     PR_WaitCondVar(
01376                         client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
01377             }
01378             PR_Unlock(client[index].ml);
01379 
01380             TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE, 
01381                 ("main(0x%p): joining client(0x%p)\n",
01382                 PR_CurrentThread(), client[index].thread));
01383 
01384                   joinStatus = JoinThread(client[index].thread);
01385                   TEST_ASSERT(PR_SUCCESS == joinStatus);
01386             PR_DestroyCondVar(client[index].stateChange);
01387             PR_DestroyLock(client[index].ml);
01388         }
01389         PR_DELETE(client);
01390     }
01391 
01392     if (NULL != server)
01393     {
01394         /* All clients joined - retrieve the server */
01395         TEST_LOG(
01396             cltsrv_log_file, TEST_LOG_NOTICE, 
01397             ("main(0x%p): notifying server(0x%p) to stop\n",
01398             PR_CurrentThread(), server->thread));
01399 
01400         PR_Lock(server->ml);
01401         server->state = cs_stop;
01402         PR_Interrupt(server->thread);
01403         while (cs_exit != server->state)
01404             PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
01405         PR_Unlock(server->ml);
01406 
01407         TEST_LOG(
01408             cltsrv_log_file, TEST_LOG_NOTICE, 
01409             ("main(0x%p): joining server(0x%p)\n",
01410             PR_CurrentThread(), server->thread));
01411         joinStatus = JoinThread(server->thread);
01412         TEST_ASSERT(PR_SUCCESS == joinStatus);
01413 
01414         PR_DestroyCondVar(server->stateChange);
01415         PR_DestroyCondVar(server->pool.exiting);
01416         PR_DestroyCondVar(server->pool.acceptComplete);
01417         PR_DestroyLock(server->ml);
01418         PR_DELETE(server);
01419     }
01420 
01421     TEST_LOG(
01422         cltsrv_log_file, TEST_LOG_ALWAYS, 
01423         ("main(0x%p): test complete\n", PR_CurrentThread()));
01424 
01425        if (thread_provider == thread_win32)
01426               thread_type = "\nWin32 Thread Statistics\n";
01427        else if (thread_provider == thread_pthread)
01428               thread_type = "\npthread Statistics\n";
01429        else if (thread_provider == thread_uithread)
01430               thread_type = "\nUnix International (UI) Thread Statistics\n";
01431        else if (thread_provider == thread_sproc)
01432               thread_type = "\nsproc Statistics\n";
01433     else {
01434               PR_ASSERT(thread_provider == thread_nspr);
01435               thread_type = "\nPRThread Statistics\nn";
01436        }
01437 
01438     PT_FPrintStats(debug_out, thread_type);
01439 
01440     TimeOfDayMessage("Test exiting at", PR_CurrentThread());
01441     PR_Cleanup();
01442     return 0;
01443 }  /* main */
01444 
01445 /* cltsrv.c */