1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/tests/cltsrv.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1182 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * 1.11 + * Notes: 1.12 + * [1] lth. The call to Sleep() is a hack to get the test case to run 1.13 + * on Windows 95. Without it, the test case fails with an error 1.14 + * WSAECONNRESET following a recv() call. The error is caused by the 1.15 + * server side thread termination without a shutdown() or closesocket() 1.16 + * call. Windows docmunentation suggests that this is predicted 1.17 + * behavior; that other platforms get away with it is ... serindipity. 1.18 + * The test case should shutdown() or closesocket() before 1.19 + * thread termination. I didn't have time to figure out where or how 1.20 + * to do it. The Sleep() call inserts enough delay to allow the 1.21 + * client side to recv() all his data before the server side thread 1.22 + * terminates. Whew! ... 1.23 + * 1.24 + ** Modification History: 1.25 + * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. 1.26 + * The debug mode will print all of the printfs associated with this test. 1.27 + * The regress mode will be the default mode. Since the regress tool limits 1.28 + * the output to a one line status:PASS or FAIL,all of the printf statements 1.29 + * have been handled with an if (debug_mode) statement. 1.30 + */ 1.31 + 1.32 +#include "prclist.h" 1.33 +#include "prcvar.h" 1.34 +#include "prerror.h" 1.35 +#include "prinit.h" 1.36 +#include "prinrval.h" 1.37 +#include "prio.h" 1.38 +#include "prlock.h" 1.39 +#include "prlog.h" 1.40 +#include "prtime.h" 1.41 +#include "prmem.h" 1.42 +#include "prnetdb.h" 1.43 +#include "prprf.h" 1.44 +#include "prthread.h" 1.45 + 1.46 +#include "pprio.h" 1.47 +#include "primpl.h" 1.48 + 1.49 +#include "plstr.h" 1.50 +#include "plerror.h" 1.51 +#include "plgetopt.h" 1.52 + 1.53 +#include <stdlib.h> 1.54 +#include <string.h> 1.55 + 1.56 +#if defined(XP_UNIX) 1.57 +#include <math.h> 1.58 +#endif 1.59 + 1.60 +/* 1.61 +** This is the beginning of the test 1.62 +*/ 1.63 + 1.64 +#define RECV_FLAGS 0 1.65 +#define SEND_FLAGS 0 1.66 +#define DEFAULT_LOW 0 1.67 +#define DEFAULT_HIGH 0 1.68 +#define BUFFER_SIZE 1024 1.69 +#define DEFAULT_BACKLOG 5 1.70 +#define DEFAULT_PORT 12849 1.71 +#define DEFAULT_CLIENTS 1 1.72 +#define ALLOWED_IN_ACCEPT 1 1.73 +#define DEFAULT_CLIPPING 1000 1.74 +#define DEFAULT_WORKERS_MIN 1 1.75 +#define DEFAULT_WORKERS_MAX 1 1.76 +#define DEFAULT_SERVER "localhost" 1.77 +#define DEFAULT_EXECUTION_TIME 10 1.78 +#define DEFAULT_CLIENT_TIMEOUT 4000 1.79 +#define DEFAULT_SERVER_TIMEOUT 4000 1.80 +#define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH 1.81 + 1.82 +typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t; 1.83 + 1.84 +static void PR_CALLBACK Worker(void *arg); 1.85 +typedef struct CSPool_s CSPool_t; 1.86 +typedef struct CSWorker_s CSWorker_t; 1.87 +typedef struct CSServer_s CSServer_t; 1.88 +typedef enum Verbosity 1.89 +{ 1.90 + TEST_LOG_ALWAYS, 1.91 + TEST_LOG_ERROR, 1.92 + TEST_LOG_WARNING, 1.93 + TEST_LOG_NOTICE, 1.94 + TEST_LOG_INFO, 1.95 + TEST_LOG_STATUS, 1.96 + TEST_LOG_VERBOSE 1.97 +} Verbosity; 1.98 + 1.99 +static PRInt32 domain = AF_INET; 1.100 +static PRInt32 protocol = 6; /* TCP */ 1.101 +static PRFileDesc *debug_out = NULL; 1.102 +static PRBool debug_mode = PR_FALSE; 1.103 +static PRBool pthread_stats = PR_FALSE; 1.104 +static Verbosity verbosity = TEST_LOG_ALWAYS; 1.105 +static PRThreadScope thread_scope = PR_LOCAL_THREAD; 1.106 + 1.107 +struct CSWorker_s 1.108 +{ 1.109 + PRCList element; /* list of the server's workers */ 1.110 + 1.111 + PRThread *thread; /* this worker objects thread */ 1.112 + CSServer_t *server; /* back pointer to server structure */ 1.113 +}; 1.114 + 1.115 +struct CSPool_s 1.116 +{ 1.117 + PRCondVar *exiting; 1.118 + PRCondVar *acceptComplete; 1.119 + PRUint32 accepting, active, workers; 1.120 +}; 1.121 + 1.122 +struct CSServer_s 1.123 +{ 1.124 + PRCList list; /* head of worker list */ 1.125 + 1.126 + PRLock *ml; 1.127 + PRThread *thread; /* the main server thread */ 1.128 + PRCondVar *stateChange; 1.129 + 1.130 + PRUint16 port; /* port we're listening on */ 1.131 + PRUint32 backlog; /* size of our listener backlog */ 1.132 + PRFileDesc *listener; /* the fd accepting connections */ 1.133 + 1.134 + CSPool_t pool; /* statistics on worker threads */ 1.135 + CSState_t state; /* the server's state */ 1.136 + struct /* controlling worker counts */ 1.137 + { 1.138 + PRUint32 minimum, maximum, accepting; 1.139 + } workers; 1.140 + 1.141 + /* statistics */ 1.142 + PRIntervalTime started, stopped; 1.143 + PRUint32 operations, bytesTransferred; 1.144 +}; 1.145 + 1.146 +typedef struct CSDescriptor_s 1.147 +{ 1.148 + PRInt32 size; /* size of transfer */ 1.149 + char filename[60]; /* filename, null padded */ 1.150 +} CSDescriptor_t; 1.151 + 1.152 +typedef struct CSClient_s 1.153 +{ 1.154 + PRLock *ml; 1.155 + PRThread *thread; 1.156 + PRCondVar *stateChange; 1.157 + PRNetAddr serverAddress; 1.158 + 1.159 + CSState_t state; 1.160 + 1.161 + /* statistics */ 1.162 + PRIntervalTime started, stopped; 1.163 + PRUint32 operations, bytesTransferred; 1.164 +} CSClient_t; 1.165 + 1.166 +#define TEST_LOG(l, p, a) \ 1.167 + do { \ 1.168 + if (debug_mode || (p <= verbosity)) printf a; \ 1.169 + } while (0) 1.170 + 1.171 +PRLogModuleInfo *cltsrv_log_file = NULL; 1.172 + 1.173 +#define MY_ASSERT(_expr) \ 1.174 + ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) 1.175 + 1.176 +#define TEST_ASSERT(_expr) \ 1.177 + ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) 1.178 + 1.179 +static void _MY_Assert(const char *s, const char *file, PRIntn ln) 1.180 +{ 1.181 + PL_PrintError(NULL); 1.182 + PR_Assert(s, file, ln); 1.183 +} /* _MY_Assert */ 1.184 + 1.185 +static PRBool Aborted(PRStatus rv) 1.186 +{ 1.187 + return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ? 1.188 + PR_TRUE : PR_FALSE; 1.189 +} 1.190 + 1.191 +static void TimeOfDayMessage(const char *msg, PRThread* me) 1.192 +{ 1.193 + char buffer[100]; 1.194 + PRExplodedTime tod; 1.195 + PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod); 1.196 + (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod); 1.197 + 1.198 + TEST_LOG( 1.199 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.200 + ("%s(0x%p): %s\n", msg, me, buffer)); 1.201 +} /* TimeOfDayMessage */ 1.202 + 1.203 + 1.204 +static void PR_CALLBACK Client(void *arg) 1.205 +{ 1.206 + PRStatus rv; 1.207 + PRIntn index; 1.208 + char buffer[1024]; 1.209 + PRFileDesc *fd = NULL; 1.210 + PRUintn clipping = DEFAULT_CLIPPING; 1.211 + PRThread *me = PR_GetCurrentThread(); 1.212 + CSClient_t *client = (CSClient_t*)arg; 1.213 + CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); 1.214 + PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT); 1.215 + 1.216 + 1.217 + for (index = 0; index < sizeof(buffer); ++index) 1.218 + buffer[index] = (char)index; 1.219 + 1.220 + client->started = PR_IntervalNow(); 1.221 + 1.222 + PR_Lock(client->ml); 1.223 + client->state = cs_run; 1.224 + PR_NotifyCondVar(client->stateChange); 1.225 + PR_Unlock(client->ml); 1.226 + 1.227 + TimeOfDayMessage("Client started at", me); 1.228 + 1.229 + while (cs_run == client->state) 1.230 + { 1.231 + PRInt32 bytes, descbytes, filebytes, netbytes; 1.232 + 1.233 + (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer)); 1.234 + TEST_LOG(cltsrv_log_file, TEST_LOG_INFO, 1.235 + ("\tClient(0x%p): connecting to server at %s\n", me, buffer)); 1.236 + 1.237 + fd = PR_Socket(domain, SOCK_STREAM, protocol); 1.238 + TEST_ASSERT(NULL != fd); 1.239 + rv = PR_Connect(fd, &client->serverAddress, timeout); 1.240 + if (PR_FAILURE == rv) 1.241 + { 1.242 + TEST_LOG( 1.243 + cltsrv_log_file, TEST_LOG_ERROR, 1.244 + ("\tClient(0x%p): conection failed (%d, %d)\n", 1.245 + me, PR_GetError(), PR_GetOSError())); 1.246 + goto aborted; 1.247 + } 1.248 + 1.249 + memset(descriptor, 0, sizeof(*descriptor)); 1.250 + descriptor->size = PR_htonl(descbytes = rand() % clipping); 1.251 + PR_snprintf( 1.252 + descriptor->filename, sizeof(descriptor->filename), 1.253 + "CS%p%p-%p.dat", client->started, me, client->operations); 1.254 + TEST_LOG( 1.255 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.256 + ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes)); 1.257 + bytes = PR_Send( 1.258 + fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout); 1.259 + if (sizeof(CSDescriptor_t) != bytes) 1.260 + { 1.261 + if (Aborted(PR_FAILURE)) goto aborted; 1.262 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.263 + { 1.264 + TEST_LOG( 1.265 + cltsrv_log_file, TEST_LOG_ERROR, 1.266 + ("\tClient(0x%p): send descriptor timeout\n", me)); 1.267 + goto retry; 1.268 + } 1.269 + } 1.270 + TEST_ASSERT(sizeof(*descriptor) == bytes); 1.271 + 1.272 + netbytes = 0; 1.273 + while (netbytes < descbytes) 1.274 + { 1.275 + filebytes = sizeof(buffer); 1.276 + if ((descbytes - netbytes) < filebytes) 1.277 + filebytes = descbytes - netbytes; 1.278 + TEST_LOG( 1.279 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.280 + ("\tClient(0x%p): sending %d bytes\n", me, filebytes)); 1.281 + bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout); 1.282 + if (filebytes != bytes) 1.283 + { 1.284 + if (Aborted(PR_FAILURE)) goto aborted; 1.285 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.286 + { 1.287 + TEST_LOG( 1.288 + cltsrv_log_file, TEST_LOG_ERROR, 1.289 + ("\tClient(0x%p): send data timeout\n", me)); 1.290 + goto retry; 1.291 + } 1.292 + } 1.293 + TEST_ASSERT(bytes == filebytes); 1.294 + netbytes += bytes; 1.295 + } 1.296 + filebytes = 0; 1.297 + while (filebytes < descbytes) 1.298 + { 1.299 + netbytes = sizeof(buffer); 1.300 + if ((descbytes - filebytes) < netbytes) 1.301 + netbytes = descbytes - filebytes; 1.302 + TEST_LOG( 1.303 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.304 + ("\tClient(0x%p): receiving %d bytes\n", me, netbytes)); 1.305 + bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); 1.306 + if (-1 == bytes) 1.307 + { 1.308 + if (Aborted(PR_FAILURE)) 1.309 + { 1.310 + TEST_LOG( 1.311 + cltsrv_log_file, TEST_LOG_ERROR, 1.312 + ("\tClient(0x%p): receive data aborted\n", me)); 1.313 + goto aborted; 1.314 + } 1.315 + else if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.316 + TEST_LOG( 1.317 + cltsrv_log_file, TEST_LOG_ERROR, 1.318 + ("\tClient(0x%p): receive data timeout\n", me)); 1.319 + else 1.320 + TEST_LOG( 1.321 + cltsrv_log_file, TEST_LOG_ERROR, 1.322 + ("\tClient(0x%p): receive error (%d, %d)\n", 1.323 + me, PR_GetError(), PR_GetOSError())); 1.324 + goto retry; 1.325 + } 1.326 + if (0 == bytes) 1.327 + { 1.328 + TEST_LOG( 1.329 + cltsrv_log_file, TEST_LOG_ERROR, 1.330 + ("\t\tClient(0x%p): unexpected end of stream\n", 1.331 + PR_GetCurrentThread())); 1.332 + break; 1.333 + } 1.334 + filebytes += bytes; 1.335 + } 1.336 + 1.337 + rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH); 1.338 + if (Aborted(rv)) goto aborted; 1.339 + TEST_ASSERT(PR_SUCCESS == rv); 1.340 +retry: 1.341 + (void)PR_Close(fd); fd = NULL; 1.342 + TEST_LOG( 1.343 + cltsrv_log_file, TEST_LOG_INFO, 1.344 + ("\tClient(0x%p): disconnected from server\n", me)); 1.345 + 1.346 + PR_Lock(client->ml); 1.347 + client->operations += 1; 1.348 + client->bytesTransferred += 2 * descbytes; 1.349 + rv = PR_WaitCondVar(client->stateChange, rand() % clipping); 1.350 + PR_Unlock(client->ml); 1.351 + if (Aborted(rv)) break; 1.352 + } 1.353 + 1.354 +aborted: 1.355 + client->stopped = PR_IntervalNow(); 1.356 + 1.357 + PR_ClearInterrupt(); 1.358 + if (NULL != fd) rv = PR_Close(fd); 1.359 + 1.360 + PR_Lock(client->ml); 1.361 + client->state = cs_exit; 1.362 + PR_NotifyCondVar(client->stateChange); 1.363 + PR_Unlock(client->ml); 1.364 + PR_DELETE(descriptor); 1.365 + TEST_LOG( 1.366 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.367 + ("\tClient(0x%p): stopped after %u operations and %u bytes\n", 1.368 + PR_GetCurrentThread(), client->operations, client->bytesTransferred)); 1.369 + 1.370 +} /* Client */ 1.371 + 1.372 +static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server) 1.373 +{ 1.374 + PRStatus drv, rv; 1.375 + char buffer[1024]; 1.376 + PRFileDesc *file = NULL; 1.377 + PRThread * me = PR_GetCurrentThread(); 1.378 + PRInt32 bytes, descbytes, netbytes, filebytes = 0; 1.379 + CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); 1.380 + PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT); 1.381 + 1.382 + TEST_LOG( 1.383 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.384 + ("\tProcessRequest(0x%p): receiving desciptor\n", me)); 1.385 + bytes = PR_Recv( 1.386 + fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout); 1.387 + if (-1 == bytes) 1.388 + { 1.389 + rv = PR_FAILURE; 1.390 + if (Aborted(rv)) goto exit; 1.391 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.392 + { 1.393 + TEST_LOG( 1.394 + cltsrv_log_file, TEST_LOG_ERROR, 1.395 + ("\tProcessRequest(0x%p): receive timeout\n", me)); 1.396 + } 1.397 + goto exit; 1.398 + } 1.399 + if (0 == bytes) 1.400 + { 1.401 + rv = PR_FAILURE; 1.402 + TEST_LOG( 1.403 + cltsrv_log_file, TEST_LOG_ERROR, 1.404 + ("\tProcessRequest(0x%p): unexpected end of file\n", me)); 1.405 + goto exit; 1.406 + } 1.407 + descbytes = PR_ntohl(descriptor->size); 1.408 + TEST_ASSERT(sizeof(*descriptor) == bytes); 1.409 + 1.410 + TEST_LOG( 1.411 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.412 + ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n", 1.413 + me, descbytes, descriptor->filename)); 1.414 + 1.415 + file = PR_Open( 1.416 + descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666); 1.417 + if (NULL == file) 1.418 + { 1.419 + rv = PR_FAILURE; 1.420 + if (Aborted(rv)) goto aborted; 1.421 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.422 + { 1.423 + TEST_LOG( 1.424 + cltsrv_log_file, TEST_LOG_ERROR, 1.425 + ("\tProcessRequest(0x%p): open file timeout\n", me)); 1.426 + goto aborted; 1.427 + } 1.428 + } 1.429 + TEST_ASSERT(NULL != file); 1.430 + 1.431 + filebytes = 0; 1.432 + while (filebytes < descbytes) 1.433 + { 1.434 + netbytes = sizeof(buffer); 1.435 + if ((descbytes - filebytes) < netbytes) 1.436 + netbytes = descbytes - filebytes; 1.437 + TEST_LOG( 1.438 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.439 + ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes)); 1.440 + bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); 1.441 + if (-1 == bytes) 1.442 + { 1.443 + rv = PR_FAILURE; 1.444 + if (Aborted(rv)) goto aborted; 1.445 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.446 + { 1.447 + TEST_LOG( 1.448 + cltsrv_log_file, TEST_LOG_ERROR, 1.449 + ("\t\tProcessRequest(0x%p): receive data timeout\n", me)); 1.450 + goto aborted; 1.451 + } 1.452 + /* 1.453 + * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED) 1.454 + * on NT here. This is equivalent to ECONNRESET on Unix. 1.455 + * -wtc 1.456 + */ 1.457 + TEST_LOG( 1.458 + cltsrv_log_file, TEST_LOG_WARNING, 1.459 + ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n", 1.460 + me, PR_GetError(), PR_GetOSError())); 1.461 + goto aborted; 1.462 + } 1.463 + if(0 == bytes) 1.464 + { 1.465 + TEST_LOG( 1.466 + cltsrv_log_file, TEST_LOG_WARNING, 1.467 + ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me)); 1.468 + rv = PR_FAILURE; 1.469 + goto aborted; 1.470 + } 1.471 + filebytes += bytes; 1.472 + netbytes = bytes; 1.473 + /* The byte count for PR_Write should be positive */ 1.474 + MY_ASSERT(netbytes > 0); 1.475 + TEST_LOG( 1.476 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.477 + ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes)); 1.478 + bytes = PR_Write(file, buffer, netbytes); 1.479 + if (netbytes != bytes) 1.480 + { 1.481 + rv = PR_FAILURE; 1.482 + if (Aborted(rv)) goto aborted; 1.483 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.484 + { 1.485 + TEST_LOG( 1.486 + cltsrv_log_file, TEST_LOG_ERROR, 1.487 + ("\t\tProcessRequest(0x%p): write file timeout\n", me)); 1.488 + goto aborted; 1.489 + } 1.490 + } 1.491 + TEST_ASSERT(bytes > 0); 1.492 + } 1.493 + 1.494 + PR_Lock(server->ml); 1.495 + server->operations += 1; 1.496 + server->bytesTransferred += filebytes; 1.497 + PR_Unlock(server->ml); 1.498 + 1.499 + rv = PR_Close(file); 1.500 + if (Aborted(rv)) goto aborted; 1.501 + TEST_ASSERT(PR_SUCCESS == rv); 1.502 + file = NULL; 1.503 + 1.504 + TEST_LOG( 1.505 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.506 + ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename)); 1.507 + file = PR_Open(descriptor->filename, PR_RDONLY, 0); 1.508 + if (NULL == file) 1.509 + { 1.510 + rv = PR_FAILURE; 1.511 + if (Aborted(rv)) goto aborted; 1.512 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.513 + { 1.514 + TEST_LOG( 1.515 + cltsrv_log_file, TEST_LOG_ERROR, 1.516 + ("\t\tProcessRequest(0x%p): open file timeout\n", 1.517 + PR_GetCurrentThread())); 1.518 + goto aborted; 1.519 + } 1.520 + TEST_LOG( 1.521 + cltsrv_log_file, TEST_LOG_ERROR, 1.522 + ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n", 1.523 + me, PR_GetError(), PR_GetOSError())); 1.524 + goto aborted; 1.525 + } 1.526 + TEST_ASSERT(NULL != file); 1.527 + 1.528 + netbytes = 0; 1.529 + while (netbytes < descbytes) 1.530 + { 1.531 + filebytes = sizeof(buffer); 1.532 + if ((descbytes - netbytes) < filebytes) 1.533 + filebytes = descbytes - netbytes; 1.534 + TEST_LOG( 1.535 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.536 + ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes)); 1.537 + bytes = PR_Read(file, buffer, filebytes); 1.538 + if (filebytes != bytes) 1.539 + { 1.540 + rv = PR_FAILURE; 1.541 + if (Aborted(rv)) goto aborted; 1.542 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.543 + TEST_LOG( 1.544 + cltsrv_log_file, TEST_LOG_ERROR, 1.545 + ("\t\tProcessRequest(0x%p): read file timeout\n", me)); 1.546 + else 1.547 + TEST_LOG( 1.548 + cltsrv_log_file, TEST_LOG_ERROR, 1.549 + ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n", 1.550 + me, PR_GetError(), PR_GetOSError())); 1.551 + goto aborted; 1.552 + } 1.553 + TEST_ASSERT(bytes > 0); 1.554 + netbytes += bytes; 1.555 + filebytes = bytes; 1.556 + TEST_LOG( 1.557 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.558 + ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes)); 1.559 + bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout); 1.560 + if (filebytes != bytes) 1.561 + { 1.562 + rv = PR_FAILURE; 1.563 + if (Aborted(rv)) goto aborted; 1.564 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.565 + { 1.566 + TEST_LOG( 1.567 + cltsrv_log_file, TEST_LOG_ERROR, 1.568 + ("\t\tProcessRequest(0x%p): send data timeout\n", me)); 1.569 + goto aborted; 1.570 + } 1.571 + break; 1.572 + } 1.573 + TEST_ASSERT(bytes > 0); 1.574 + } 1.575 + 1.576 + PR_Lock(server->ml); 1.577 + server->bytesTransferred += filebytes; 1.578 + PR_Unlock(server->ml); 1.579 + 1.580 + rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH); 1.581 + if (Aborted(rv)) goto aborted; 1.582 + 1.583 + rv = PR_Close(file); 1.584 + if (Aborted(rv)) goto aborted; 1.585 + TEST_ASSERT(PR_SUCCESS == rv); 1.586 + file = NULL; 1.587 + 1.588 +aborted: 1.589 + PR_ClearInterrupt(); 1.590 + if (NULL != file) PR_Close(file); 1.591 + drv = PR_Delete(descriptor->filename); 1.592 + TEST_ASSERT(PR_SUCCESS == drv); 1.593 +exit: 1.594 + TEST_LOG( 1.595 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.596 + ("\t\tProcessRequest(0x%p): Finished\n", me)); 1.597 + 1.598 + PR_DELETE(descriptor); 1.599 + 1.600 +#if defined(WIN95) 1.601 + PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */ 1.602 +#endif 1.603 + return rv; 1.604 +} /* ProcessRequest */ 1.605 + 1.606 +static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool) 1.607 +{ 1.608 + CSWorker_t *worker = PR_NEWZAP(CSWorker_t); 1.609 + worker->server = server; 1.610 + PR_INIT_CLIST(&worker->element); 1.611 + worker->thread = PR_CreateThread( 1.612 + PR_USER_THREAD, Worker, worker, 1.613 + DEFAULT_SERVER_PRIORITY, thread_scope, 1.614 + PR_UNJOINABLE_THREAD, 0); 1.615 + if (NULL == worker->thread) 1.616 + { 1.617 + PR_DELETE(worker); 1.618 + return PR_FAILURE; 1.619 + } 1.620 + 1.621 + TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, 1.622 + ("\tCreateWorker(0x%p): create new worker (0x%p)\n", 1.623 + PR_GetCurrentThread(), worker->thread)); 1.624 + 1.625 + return PR_SUCCESS; 1.626 +} /* CreateWorker */ 1.627 + 1.628 +static void PR_CALLBACK Worker(void *arg) 1.629 +{ 1.630 + PRStatus rv; 1.631 + PRNetAddr from; 1.632 + PRFileDesc *fd = NULL; 1.633 + PRThread *me = PR_GetCurrentThread(); 1.634 + CSWorker_t *worker = (CSWorker_t*)arg; 1.635 + CSServer_t *server = worker->server; 1.636 + CSPool_t *pool = &server->pool; 1.637 + 1.638 + TEST_LOG( 1.639 + cltsrv_log_file, TEST_LOG_NOTICE, 1.640 + ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1)); 1.641 + 1.642 + PR_Lock(server->ml); 1.643 + PR_APPEND_LINK(&worker->element, &server->list); 1.644 + pool->workers += 1; /* define our existance */ 1.645 + 1.646 + while (cs_run == server->state) 1.647 + { 1.648 + while (pool->accepting >= server->workers.accepting) 1.649 + { 1.650 + TEST_LOG( 1.651 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.652 + ("\t\tWorker(0x%p): waiting for accept slot[%d]\n", 1.653 + me, pool->accepting)); 1.654 + rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT); 1.655 + if (Aborted(rv) || (cs_run != server->state)) 1.656 + { 1.657 + TEST_LOG( 1.658 + cltsrv_log_file, TEST_LOG_NOTICE, 1.659 + ("\tWorker(0x%p): has been %s\n", 1.660 + me, (Aborted(rv) ? "interrupted" : "stopped"))); 1.661 + goto exit; 1.662 + } 1.663 + } 1.664 + pool->accepting += 1; /* how many are really in accept */ 1.665 + PR_Unlock(server->ml); 1.666 + 1.667 + TEST_LOG( 1.668 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.669 + ("\t\tWorker(0x%p): calling accept\n", me)); 1.670 + fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT); 1.671 + 1.672 + PR_Lock(server->ml); 1.673 + pool->accepting -= 1; 1.674 + PR_NotifyCondVar(pool->acceptComplete); 1.675 + 1.676 + if ((NULL == fd) && Aborted(PR_FAILURE)) 1.677 + { 1.678 + if (NULL != server->listener) 1.679 + { 1.680 + PR_Close(server->listener); 1.681 + server->listener = NULL; 1.682 + } 1.683 + goto exit; 1.684 + } 1.685 + 1.686 + if (NULL != fd) 1.687 + { 1.688 + /* 1.689 + ** Create another worker of the total number of workers is 1.690 + ** less than the minimum specified or we have none left in 1.691 + ** accept() AND we're not over the maximum. 1.692 + ** This sort of presumes that the number allowed in accept 1.693 + ** is at least as many as the minimum. Otherwise we'll keep 1.694 + ** creating new threads and deleting them soon after. 1.695 + */ 1.696 + PRBool another = 1.697 + ((pool->workers < server->workers.minimum) || 1.698 + ((0 == pool->accepting) 1.699 + && (pool->workers < server->workers.maximum))) ? 1.700 + PR_TRUE : PR_FALSE; 1.701 + pool->active += 1; 1.702 + PR_Unlock(server->ml); 1.703 + 1.704 + if (another) (void)CreateWorker(server, pool); 1.705 + 1.706 + rv = ProcessRequest(fd, server); 1.707 + if (PR_SUCCESS != rv) 1.708 + TEST_LOG( 1.709 + cltsrv_log_file, TEST_LOG_ERROR, 1.710 + ("\t\tWorker(0x%p): server process ended abnormally\n", me)); 1.711 + (void)PR_Close(fd); fd = NULL; 1.712 + 1.713 + PR_Lock(server->ml); 1.714 + pool->active -= 1; 1.715 + } 1.716 + } 1.717 + 1.718 +exit: 1.719 + PR_ClearInterrupt(); 1.720 + PR_Unlock(server->ml); 1.721 + 1.722 + if (NULL != fd) 1.723 + { 1.724 + (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH); 1.725 + (void)PR_Close(fd); 1.726 + } 1.727 + 1.728 + TEST_LOG( 1.729 + cltsrv_log_file, TEST_LOG_NOTICE, 1.730 + ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers)); 1.731 + 1.732 + PR_Lock(server->ml); 1.733 + pool->workers -= 1; /* undefine our existance */ 1.734 + PR_REMOVE_AND_INIT_LINK(&worker->element); 1.735 + PR_NotifyCondVar(pool->exiting); 1.736 + PR_Unlock(server->ml); 1.737 + 1.738 + PR_DELETE(worker); /* destruction of the "worker" object */ 1.739 + 1.740 +} /* Worker */ 1.741 + 1.742 +static void PR_CALLBACK Server(void *arg) 1.743 +{ 1.744 + PRStatus rv; 1.745 + PRNetAddr serverAddress; 1.746 + PRThread *me = PR_GetCurrentThread(); 1.747 + CSServer_t *server = (CSServer_t*)arg; 1.748 + PRSocketOptionData sockOpt; 1.749 + 1.750 + server->listener = PR_Socket(domain, SOCK_STREAM, protocol); 1.751 + 1.752 + sockOpt.option = PR_SockOpt_Reuseaddr; 1.753 + sockOpt.value.reuse_addr = PR_TRUE; 1.754 + rv = PR_SetSocketOption(server->listener, &sockOpt); 1.755 + TEST_ASSERT(PR_SUCCESS == rv); 1.756 + 1.757 + memset(&serverAddress, 0, sizeof(serverAddress)); 1.758 + if (PR_AF_INET6 != domain) 1.759 + rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress); 1.760 + else 1.761 + rv = PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, DEFAULT_PORT, 1.762 + &serverAddress); 1.763 + rv = PR_Bind(server->listener, &serverAddress); 1.764 + TEST_ASSERT(PR_SUCCESS == rv); 1.765 + 1.766 + rv = PR_Listen(server->listener, server->backlog); 1.767 + TEST_ASSERT(PR_SUCCESS == rv); 1.768 + 1.769 + server->started = PR_IntervalNow(); 1.770 + TimeOfDayMessage("Server started at", me); 1.771 + 1.772 + PR_Lock(server->ml); 1.773 + server->state = cs_run; 1.774 + PR_NotifyCondVar(server->stateChange); 1.775 + PR_Unlock(server->ml); 1.776 + 1.777 + /* 1.778 + ** Create the first worker (actually, a thread that accepts 1.779 + ** connections and then processes the work load as needed). 1.780 + ** From this point on, additional worker threads are created 1.781 + ** as they are needed by existing worker threads. 1.782 + */ 1.783 + rv = CreateWorker(server, &server->pool); 1.784 + TEST_ASSERT(PR_SUCCESS == rv); 1.785 + 1.786 + /* 1.787 + ** From here on this thread is merely hanging around as the contact 1.788 + ** point for the main test driver. It's just waiting for the driver 1.789 + ** to declare the test complete. 1.790 + */ 1.791 + TEST_LOG( 1.792 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.793 + ("\tServer(0x%p): waiting for state change\n", me)); 1.794 + 1.795 + PR_Lock(server->ml); 1.796 + while ((cs_run == server->state) && !Aborted(rv)) 1.797 + { 1.798 + rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); 1.799 + } 1.800 + PR_Unlock(server->ml); 1.801 + PR_ClearInterrupt(); 1.802 + 1.803 + TEST_LOG( 1.804 + cltsrv_log_file, TEST_LOG_INFO, 1.805 + ("\tServer(0x%p): shutting down workers\n", me)); 1.806 + 1.807 + /* 1.808 + ** Get all the worker threads to exit. They know how to 1.809 + ** clean up after themselves, so this is just a matter of 1.810 + ** waiting for clorine in the pool to take effect. During 1.811 + ** this stage we're ignoring interrupts. 1.812 + */ 1.813 + server->workers.minimum = server->workers.maximum = 0; 1.814 + 1.815 + PR_Lock(server->ml); 1.816 + while (!PR_CLIST_IS_EMPTY(&server->list)) 1.817 + { 1.818 + PRCList *head = PR_LIST_HEAD(&server->list); 1.819 + CSWorker_t *worker = (CSWorker_t*)head; 1.820 + TEST_LOG( 1.821 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.822 + ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker)); 1.823 + rv = PR_Interrupt(worker->thread); 1.824 + TEST_ASSERT(PR_SUCCESS == rv); 1.825 + PR_REMOVE_AND_INIT_LINK(head); 1.826 + } 1.827 + 1.828 + while (server->pool.workers > 0) 1.829 + { 1.830 + TEST_LOG( 1.831 + cltsrv_log_file, TEST_LOG_NOTICE, 1.832 + ("\tServer(0x%p): waiting for %u workers to exit\n", 1.833 + me, server->pool.workers)); 1.834 + (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT); 1.835 + } 1.836 + 1.837 + server->state = cs_exit; 1.838 + PR_NotifyCondVar(server->stateChange); 1.839 + PR_Unlock(server->ml); 1.840 + 1.841 + TEST_LOG( 1.842 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.843 + ("\tServer(0x%p): stopped after %u operations and %u bytes\n", 1.844 + me, server->operations, server->bytesTransferred)); 1.845 + 1.846 + if (NULL != server->listener) PR_Close(server->listener); 1.847 + server->stopped = PR_IntervalNow(); 1.848 + 1.849 +} /* Server */ 1.850 + 1.851 +static void WaitForCompletion(PRIntn execution) 1.852 +{ 1.853 + while (execution > 0) 1.854 + { 1.855 + PRIntn dally = (execution > 30) ? 30 : execution; 1.856 + PR_Sleep(PR_SecondsToInterval(dally)); 1.857 + if (pthread_stats) PT_FPrintStats(debug_out, "\nPThread Statistics\n"); 1.858 + execution -= dally; 1.859 + } 1.860 +} /* WaitForCompletion */ 1.861 + 1.862 +static void Help(void) 1.863 +{ 1.864 + PR_fprintf(debug_out, "cltsrv test program usage:\n"); 1.865 + PR_fprintf(debug_out, "\t-a <n> threads allowed in accept (5)\n"); 1.866 + PR_fprintf(debug_out, "\t-b <n> backlock for listen (5)\n"); 1.867 + PR_fprintf(debug_out, "\t-c <threads> number of clients to create (1)\n"); 1.868 + PR_fprintf(debug_out, "\t-f <low> low water mark for fd caching (0)\n"); 1.869 + PR_fprintf(debug_out, "\t-F <high> high water mark for fd caching (0)\n"); 1.870 + PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n"); 1.871 + PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n"); 1.872 + PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds (10)\n"); 1.873 + PR_fprintf(debug_out, "\t-s <string> dsn name of server (localhost)\n"); 1.874 + PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n"); 1.875 + PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n"); 1.876 + PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n"); 1.877 + PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n"); 1.878 + PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n"); 1.879 + PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n"); 1.880 + PR_fprintf(debug_out, "\t-h this message\n"); 1.881 +} /* Help */ 1.882 + 1.883 +static Verbosity IncrementVerbosity(void) 1.884 +{ 1.885 + PRIntn verboge = (PRIntn)verbosity + 1; 1.886 + return (Verbosity)verboge; 1.887 +} /* IncrementVerbosity */ 1.888 + 1.889 +int main(int argc, char** argv) 1.890 +{ 1.891 + PRUintn index; 1.892 + PRBool boolean; 1.893 + CSClient_t *client; 1.894 + PRStatus rv, joinStatus; 1.895 + CSServer_t *server = NULL; 1.896 + 1.897 + PRUintn backlog = DEFAULT_BACKLOG; 1.898 + PRUintn clients = DEFAULT_CLIENTS; 1.899 + const char *serverName = DEFAULT_SERVER; 1.900 + PRBool serverIsLocal = PR_TRUE; 1.901 + PRUintn accepting = ALLOWED_IN_ACCEPT; 1.902 + PRUintn workersMin = DEFAULT_WORKERS_MIN; 1.903 + PRUintn workersMax = DEFAULT_WORKERS_MAX; 1.904 + PRIntn execution = DEFAULT_EXECUTION_TIME; 1.905 + PRIntn low = DEFAULT_LOW, high = DEFAULT_HIGH; 1.906 + 1.907 + /* 1.908 + * -G use global threads 1.909 + * -a <n> threads allowed in accept 1.910 + * -b <n> backlock for listen 1.911 + * -c <threads> number of clients to create 1.912 + * -f <low> low water mark for caching FDs 1.913 + * -F <high> high water mark for caching FDs 1.914 + * -w <threads> minimal number of server threads 1.915 + * -W <threads> maximum number of server threads 1.916 + * -e <seconds> duration of the test in seconds 1.917 + * -s <string> dsn name of server (implies no server here) 1.918 + * -v verbosity 1.919 + */ 1.920 + 1.921 + PLOptStatus os; 1.922 + PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:f:F:w:W:e:s:vdhp"); 1.923 + 1.924 + debug_out = PR_GetSpecialFD(PR_StandardError); 1.925 + 1.926 + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 1.927 + { 1.928 + if (PL_OPT_BAD == os) continue; 1.929 + switch (opt->option) 1.930 + { 1.931 + case 'G': /* use global threads */ 1.932 + thread_scope = PR_GLOBAL_THREAD; 1.933 + break; 1.934 + case 'X': /* use XTP as transport */ 1.935 + protocol = 36; 1.936 + break; 1.937 + case '6': /* Use IPv6 */ 1.938 + domain = PR_AF_INET6; 1.939 + break; 1.940 + case 'a': /* the value for accepting */ 1.941 + accepting = atoi(opt->value); 1.942 + break; 1.943 + case 'b': /* the value for backlock */ 1.944 + backlog = atoi(opt->value); 1.945 + break; 1.946 + case 'c': /* number of client threads */ 1.947 + clients = atoi(opt->value); 1.948 + break; 1.949 + case 'f': /* low water fd cache */ 1.950 + low = atoi(opt->value); 1.951 + break; 1.952 + case 'F': /* low water fd cache */ 1.953 + high = atoi(opt->value); 1.954 + break; 1.955 + case 'w': /* minimum server worker threads */ 1.956 + workersMin = atoi(opt->value); 1.957 + break; 1.958 + case 'W': /* maximum server worker threads */ 1.959 + workersMax = atoi(opt->value); 1.960 + break; 1.961 + case 'e': /* program execution time in seconds */ 1.962 + execution = atoi(opt->value); 1.963 + break; 1.964 + case 's': /* server's address */ 1.965 + serverName = opt->value; 1.966 + break; 1.967 + case 'v': /* verbosity */ 1.968 + verbosity = IncrementVerbosity(); 1.969 + break; 1.970 + case 'd': /* debug mode */ 1.971 + debug_mode = PR_TRUE; 1.972 + break; 1.973 + case 'p': /* pthread mode */ 1.974 + pthread_stats = PR_TRUE; 1.975 + break; 1.976 + case 'h': 1.977 + default: 1.978 + Help(); 1.979 + return 2; 1.980 + } 1.981 + } 1.982 + PL_DestroyOptState(opt); 1.983 + 1.984 + if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) serverIsLocal = PR_FALSE; 1.985 + if (0 == execution) execution = DEFAULT_EXECUTION_TIME; 1.986 + if (0 == workersMax) workersMax = DEFAULT_WORKERS_MAX; 1.987 + if (0 == workersMin) workersMin = DEFAULT_WORKERS_MIN; 1.988 + if (0 == accepting) accepting = ALLOWED_IN_ACCEPT; 1.989 + if (0 == backlog) backlog = DEFAULT_BACKLOG; 1.990 + 1.991 + if (workersMin > accepting) accepting = workersMin; 1.992 + 1.993 + PR_STDIO_INIT(); 1.994 + TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread()); 1.995 + 1.996 + cltsrv_log_file = PR_NewLogModule("cltsrv_log"); 1.997 + MY_ASSERT(NULL != cltsrv_log_file); 1.998 + boolean = PR_SetLogFile("cltsrv.log"); 1.999 + MY_ASSERT(boolean); 1.1000 + 1.1001 + rv = PR_SetFDCacheSize(low, high); 1.1002 + PR_ASSERT(PR_SUCCESS == rv); 1.1003 + 1.1004 + if (serverIsLocal) 1.1005 + { 1.1006 + /* Establish the server */ 1.1007 + TEST_LOG( 1.1008 + cltsrv_log_file, TEST_LOG_INFO, 1.1009 + ("main(0x%p): starting server\n", PR_GetCurrentThread())); 1.1010 + 1.1011 + server = PR_NEWZAP(CSServer_t); 1.1012 + PR_INIT_CLIST(&server->list); 1.1013 + server->state = cs_init; 1.1014 + server->ml = PR_NewLock(); 1.1015 + server->backlog = backlog; 1.1016 + server->port = DEFAULT_PORT; 1.1017 + server->workers.minimum = workersMin; 1.1018 + server->workers.maximum = workersMax; 1.1019 + server->workers.accepting = accepting; 1.1020 + server->stateChange = PR_NewCondVar(server->ml); 1.1021 + server->pool.exiting = PR_NewCondVar(server->ml); 1.1022 + server->pool.acceptComplete = PR_NewCondVar(server->ml); 1.1023 + 1.1024 + TEST_LOG( 1.1025 + cltsrv_log_file, TEST_LOG_NOTICE, 1.1026 + ("main(0x%p): creating server thread\n", PR_GetCurrentThread())); 1.1027 + 1.1028 + server->thread = PR_CreateThread( 1.1029 + PR_USER_THREAD, Server, server, PR_PRIORITY_HIGH, 1.1030 + thread_scope, PR_JOINABLE_THREAD, 0); 1.1031 + TEST_ASSERT(NULL != server->thread); 1.1032 + 1.1033 + TEST_LOG( 1.1034 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.1035 + ("main(0x%p): waiting for server init\n", PR_GetCurrentThread())); 1.1036 + 1.1037 + PR_Lock(server->ml); 1.1038 + while (server->state == cs_init) 1.1039 + PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); 1.1040 + PR_Unlock(server->ml); 1.1041 + 1.1042 + TEST_LOG( 1.1043 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.1044 + ("main(0x%p): server init complete (port #%d)\n", 1.1045 + PR_GetCurrentThread(), server->port)); 1.1046 + } 1.1047 + 1.1048 + if (clients != 0) 1.1049 + { 1.1050 + /* Create all of the clients */ 1.1051 + PRHostEnt host; 1.1052 + char buffer[BUFFER_SIZE]; 1.1053 + client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t)); 1.1054 + 1.1055 + TEST_LOG( 1.1056 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.1057 + ("main(0x%p): creating %d client threads\n", 1.1058 + PR_GetCurrentThread(), clients)); 1.1059 + 1.1060 + if (!serverIsLocal) 1.1061 + { 1.1062 + rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host); 1.1063 + if (PR_SUCCESS != rv) 1.1064 + { 1.1065 + PL_FPrintError(PR_STDERR, "PR_GetHostByName"); 1.1066 + return 2; 1.1067 + } 1.1068 + } 1.1069 + 1.1070 + for (index = 0; index < clients; ++index) 1.1071 + { 1.1072 + client[index].state = cs_init; 1.1073 + client[index].ml = PR_NewLock(); 1.1074 + if (serverIsLocal) 1.1075 + { 1.1076 + if (PR_AF_INET6 != domain) 1.1077 + (void)PR_InitializeNetAddr( 1.1078 + PR_IpAddrLoopback, DEFAULT_PORT, 1.1079 + &client[index].serverAddress); 1.1080 + else 1.1081 + rv = PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6, 1.1082 + DEFAULT_PORT, &client[index].serverAddress); 1.1083 + } 1.1084 + else 1.1085 + { 1.1086 + (void)PR_EnumerateHostEnt( 1.1087 + 0, &host, DEFAULT_PORT, &client[index].serverAddress); 1.1088 + } 1.1089 + client[index].stateChange = PR_NewCondVar(client[index].ml); 1.1090 + TEST_LOG( 1.1091 + cltsrv_log_file, TEST_LOG_INFO, 1.1092 + ("main(0x%p): creating client threads\n", PR_GetCurrentThread())); 1.1093 + client[index].thread = PR_CreateThread( 1.1094 + PR_USER_THREAD, Client, &client[index], PR_PRIORITY_NORMAL, 1.1095 + thread_scope, PR_JOINABLE_THREAD, 0); 1.1096 + TEST_ASSERT(NULL != client[index].thread); 1.1097 + PR_Lock(client[index].ml); 1.1098 + while (cs_init == client[index].state) 1.1099 + PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); 1.1100 + PR_Unlock(client[index].ml); 1.1101 + } 1.1102 + } 1.1103 + 1.1104 + /* Then just let them go at it for a bit */ 1.1105 + TEST_LOG( 1.1106 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.1107 + ("main(0x%p): waiting for execution interval (%d seconds)\n", 1.1108 + PR_GetCurrentThread(), execution)); 1.1109 + 1.1110 + WaitForCompletion(execution); 1.1111 + 1.1112 + TimeOfDayMessage("Shutting down", PR_GetCurrentThread()); 1.1113 + 1.1114 + if (clients != 0) 1.1115 + { 1.1116 + for (index = 0; index < clients; ++index) 1.1117 + { 1.1118 + TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, 1.1119 + ("main(0x%p): notifying client(0x%p) to stop\n", 1.1120 + PR_GetCurrentThread(), client[index].thread)); 1.1121 + 1.1122 + PR_Lock(client[index].ml); 1.1123 + if (cs_run == client[index].state) 1.1124 + { 1.1125 + client[index].state = cs_stop; 1.1126 + PR_Interrupt(client[index].thread); 1.1127 + while (cs_stop == client[index].state) 1.1128 + PR_WaitCondVar( 1.1129 + client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); 1.1130 + } 1.1131 + PR_Unlock(client[index].ml); 1.1132 + 1.1133 + TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE, 1.1134 + ("main(0x%p): joining client(0x%p)\n", 1.1135 + PR_GetCurrentThread(), client[index].thread)); 1.1136 + 1.1137 + joinStatus = PR_JoinThread(client[index].thread); 1.1138 + TEST_ASSERT(PR_SUCCESS == joinStatus); 1.1139 + PR_DestroyCondVar(client[index].stateChange); 1.1140 + PR_DestroyLock(client[index].ml); 1.1141 + } 1.1142 + PR_DELETE(client); 1.1143 + } 1.1144 + 1.1145 + if (NULL != server) 1.1146 + { 1.1147 + /* All clients joined - retrieve the server */ 1.1148 + TEST_LOG( 1.1149 + cltsrv_log_file, TEST_LOG_NOTICE, 1.1150 + ("main(0x%p): notifying server(0x%p) to stop\n", 1.1151 + PR_GetCurrentThread(), server->thread)); 1.1152 + 1.1153 + PR_Lock(server->ml); 1.1154 + server->state = cs_stop; 1.1155 + PR_Interrupt(server->thread); 1.1156 + while (cs_exit != server->state) 1.1157 + PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); 1.1158 + PR_Unlock(server->ml); 1.1159 + 1.1160 + TEST_LOG( 1.1161 + cltsrv_log_file, TEST_LOG_NOTICE, 1.1162 + ("main(0x%p): joining server(0x%p)\n", 1.1163 + PR_GetCurrentThread(), server->thread)); 1.1164 + joinStatus = PR_JoinThread(server->thread); 1.1165 + TEST_ASSERT(PR_SUCCESS == joinStatus); 1.1166 + 1.1167 + PR_DestroyCondVar(server->stateChange); 1.1168 + PR_DestroyCondVar(server->pool.exiting); 1.1169 + PR_DestroyCondVar(server->pool.acceptComplete); 1.1170 + PR_DestroyLock(server->ml); 1.1171 + PR_DELETE(server); 1.1172 + } 1.1173 + 1.1174 + TEST_LOG( 1.1175 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.1176 + ("main(0x%p): test complete\n", PR_GetCurrentThread())); 1.1177 + 1.1178 + PT_FPrintStats(debug_out, "\nPThread Statistics\n"); 1.1179 + 1.1180 + TimeOfDayMessage("Test exiting at", PR_GetCurrentThread()); 1.1181 + PR_Cleanup(); 1.1182 + return 0; 1.1183 +} /* main */ 1.1184 + 1.1185 +/* cltsrv.c */