1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/tests/provider.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1354 @@ 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 BUFFER_SIZE 1024 1.67 +#define DEFAULT_BACKLOG 5 1.68 +#define DEFAULT_PORT 13000 1.69 +#define DEFAULT_CLIENTS 1 1.70 +#define ALLOWED_IN_ACCEPT 1 1.71 +#define DEFAULT_CLIPPING 1000 1.72 +#define DEFAULT_WORKERS_MIN 1 1.73 +#define DEFAULT_WORKERS_MAX 1 1.74 +#define DEFAULT_SERVER "localhost" 1.75 +#define DEFAULT_EXECUTION_TIME 10 1.76 +#define DEFAULT_CLIENT_TIMEOUT 4000 1.77 +#define DEFAULT_SERVER_TIMEOUT 4000 1.78 +#define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH 1.79 + 1.80 +typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t; 1.81 + 1.82 +static void PR_CALLBACK Worker(void *arg); 1.83 +typedef struct CSPool_s CSPool_t; 1.84 +typedef struct CSWorker_s CSWorker_t; 1.85 +typedef struct CSServer_s CSServer_t; 1.86 +typedef enum Verbosity 1.87 +{ 1.88 + TEST_LOG_ALWAYS, 1.89 + TEST_LOG_ERROR, 1.90 + TEST_LOG_WARNING, 1.91 + TEST_LOG_NOTICE, 1.92 + TEST_LOG_INFO, 1.93 + TEST_LOG_STATUS, 1.94 + TEST_LOG_VERBOSE 1.95 +} Verbosity; 1.96 + 1.97 +static enum { 1.98 + thread_nspr, thread_pthread, thread_sproc, thread_win32 1.99 +} thread_provider; 1.100 + 1.101 +static PRInt32 domain = AF_INET; 1.102 +static PRInt32 protocol = 6; /* TCP */ 1.103 +static PRFileDesc *debug_out = NULL; 1.104 +static PRBool debug_mode = PR_FALSE; 1.105 +static PRBool pthread_stats = PR_FALSE; 1.106 +static Verbosity verbosity = TEST_LOG_ALWAYS; 1.107 +static PRThreadScope thread_scope = PR_LOCAL_THREAD; 1.108 + 1.109 +struct CSWorker_s 1.110 +{ 1.111 + PRCList element; /* list of the server's workers */ 1.112 + 1.113 + PRThread *thread; /* this worker objects thread */ 1.114 + CSServer_t *server; /* back pointer to server structure */ 1.115 +}; 1.116 + 1.117 +struct CSPool_s 1.118 +{ 1.119 + PRCondVar *exiting; 1.120 + PRCondVar *acceptComplete; 1.121 + PRUint32 accepting, active, workers; 1.122 +}; 1.123 + 1.124 +struct CSServer_s 1.125 +{ 1.126 + PRCList list; /* head of worker list */ 1.127 + 1.128 + PRLock *ml; 1.129 + PRThread *thread; /* the main server thread */ 1.130 + PRCondVar *stateChange; 1.131 + 1.132 + PRUint16 port; /* port we're listening on */ 1.133 + PRUint32 backlog; /* size of our listener backlog */ 1.134 + PRFileDesc *listener; /* the fd accepting connections */ 1.135 + 1.136 + CSPool_t pool; /* statistics on worker threads */ 1.137 + CSState_t state; /* the server's state */ 1.138 + struct /* controlling worker counts */ 1.139 + { 1.140 + PRUint32 minimum, maximum, accepting; 1.141 + } workers; 1.142 + 1.143 + /* statistics */ 1.144 + PRIntervalTime started, stopped; 1.145 + PRUint32 operations, bytesTransferred; 1.146 +}; 1.147 + 1.148 +typedef struct CSDescriptor_s 1.149 +{ 1.150 + PRInt32 size; /* size of transfer */ 1.151 + char filename[60]; /* filename, null padded */ 1.152 +} CSDescriptor_t; 1.153 + 1.154 +typedef struct CSClient_s 1.155 +{ 1.156 + PRLock *ml; 1.157 + PRThread *thread; 1.158 + PRCondVar *stateChange; 1.159 + PRNetAddr serverAddress; 1.160 + 1.161 + CSState_t state; 1.162 + 1.163 + /* statistics */ 1.164 + PRIntervalTime started, stopped; 1.165 + PRUint32 operations, bytesTransferred; 1.166 +} CSClient_t; 1.167 + 1.168 +#define TEST_LOG(l, p, a) \ 1.169 + do { \ 1.170 + if (debug_mode || (p <= verbosity)) printf a; \ 1.171 + } while (0) 1.172 + 1.173 +PRLogModuleInfo *cltsrv_log_file = NULL; 1.174 + 1.175 +#define MY_ASSERT(_expr) \ 1.176 + ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) 1.177 + 1.178 +#define TEST_ASSERT(_expr) \ 1.179 + ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) 1.180 + 1.181 +static void _MY_Assert(const char *s, const char *file, PRIntn ln) 1.182 +{ 1.183 + PL_PrintError(NULL); 1.184 + PR_Assert(s, file, ln); 1.185 +} /* _MY_Assert */ 1.186 + 1.187 +static PRBool Aborted(PRStatus rv) 1.188 +{ 1.189 + return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ? 1.190 + PR_TRUE : PR_FALSE; 1.191 +} 1.192 + 1.193 +static void TimeOfDayMessage(const char *msg, PRThread* me) 1.194 +{ 1.195 + char buffer[100]; 1.196 + PRExplodedTime tod; 1.197 + PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod); 1.198 + (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod); 1.199 + 1.200 + TEST_LOG( 1.201 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.202 + ("%s(0x%p): %s\n", msg, me, buffer)); 1.203 +} /* TimeOfDayMessage */ 1.204 + 1.205 + 1.206 +static void PR_CALLBACK Client(void *arg) 1.207 +{ 1.208 + PRStatus rv; 1.209 + PRIntn index; 1.210 + char buffer[1024]; 1.211 + PRFileDesc *fd = NULL; 1.212 + PRUintn clipping = DEFAULT_CLIPPING; 1.213 + CSClient_t *client = (CSClient_t*)arg; 1.214 + PRThread *me = client->thread = PR_GetCurrentThread(); 1.215 + CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); 1.216 + PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT); 1.217 + 1.218 + 1.219 + for (index = 0; index < sizeof(buffer); ++index) 1.220 + buffer[index] = (char)index; 1.221 + 1.222 + client->started = PR_IntervalNow(); 1.223 + 1.224 + PR_Lock(client->ml); 1.225 + client->state = cs_run; 1.226 + PR_NotifyCondVar(client->stateChange); 1.227 + PR_Unlock(client->ml); 1.228 + 1.229 + TimeOfDayMessage("Client started at", me); 1.230 + 1.231 + while (cs_run == client->state) 1.232 + { 1.233 + PRInt32 bytes, descbytes, filebytes, netbytes; 1.234 + 1.235 + (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer)); 1.236 + TEST_LOG(cltsrv_log_file, TEST_LOG_INFO, 1.237 + ("\tClient(0x%p): connecting to server at %s\n", me, buffer)); 1.238 + 1.239 + fd = PR_Socket(domain, SOCK_STREAM, protocol); 1.240 + TEST_ASSERT(NULL != fd); 1.241 + rv = PR_Connect(fd, &client->serverAddress, timeout); 1.242 + if (PR_FAILURE == rv) 1.243 + { 1.244 + TEST_LOG( 1.245 + cltsrv_log_file, TEST_LOG_ERROR, 1.246 + ("\tClient(0x%p): conection failed\n", me)); 1.247 + goto aborted; 1.248 + } 1.249 + 1.250 + memset(descriptor, 0, sizeof(*descriptor)); 1.251 + descriptor->size = PR_htonl(descbytes = rand() % clipping); 1.252 + PR_snprintf( 1.253 + descriptor->filename, sizeof(descriptor->filename), 1.254 + "CS%p%p-%p.dat", client->started, me, client->operations); 1.255 + TEST_LOG( 1.256 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.257 + ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes)); 1.258 + bytes = PR_Send( 1.259 + fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout); 1.260 + if (sizeof(CSDescriptor_t) != bytes) 1.261 + { 1.262 + if (Aborted(PR_FAILURE)) goto aborted; 1.263 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.264 + { 1.265 + TEST_LOG( 1.266 + cltsrv_log_file, TEST_LOG_ERROR, 1.267 + ("\tClient(0x%p): send descriptor timeout\n", me)); 1.268 + goto retry; 1.269 + } 1.270 + } 1.271 + TEST_ASSERT(sizeof(*descriptor) == bytes); 1.272 + 1.273 + netbytes = 0; 1.274 + while (netbytes < descbytes) 1.275 + { 1.276 + filebytes = sizeof(buffer); 1.277 + if ((descbytes - netbytes) < filebytes) 1.278 + filebytes = descbytes - netbytes; 1.279 + TEST_LOG( 1.280 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.281 + ("\tClient(0x%p): sending %d bytes\n", me, filebytes)); 1.282 + bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout); 1.283 + if (filebytes != bytes) 1.284 + { 1.285 + if (Aborted(PR_FAILURE)) goto aborted; 1.286 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.287 + { 1.288 + TEST_LOG( 1.289 + cltsrv_log_file, TEST_LOG_ERROR, 1.290 + ("\tClient(0x%p): send data timeout\n", me)); 1.291 + goto retry; 1.292 + } 1.293 + } 1.294 + TEST_ASSERT(bytes == filebytes); 1.295 + netbytes += bytes; 1.296 + } 1.297 + filebytes = 0; 1.298 + while (filebytes < descbytes) 1.299 + { 1.300 + netbytes = sizeof(buffer); 1.301 + if ((descbytes - filebytes) < netbytes) 1.302 + netbytes = descbytes - filebytes; 1.303 + TEST_LOG( 1.304 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.305 + ("\tClient(0x%p): receiving %d bytes\n", me, netbytes)); 1.306 + bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); 1.307 + if (-1 == bytes) 1.308 + { 1.309 + if (Aborted(PR_FAILURE)) 1.310 + { 1.311 + TEST_LOG( 1.312 + cltsrv_log_file, TEST_LOG_ERROR, 1.313 + ("\tClient(0x%p): receive data aborted\n", me)); 1.314 + goto aborted; 1.315 + } 1.316 + else if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.317 + TEST_LOG( 1.318 + cltsrv_log_file, TEST_LOG_ERROR, 1.319 + ("\tClient(0x%p): receive data timeout\n", me)); 1.320 + else 1.321 + TEST_LOG( 1.322 + cltsrv_log_file, TEST_LOG_ERROR, 1.323 + ("\tClient(0x%p): receive error (%d, %d)\n", 1.324 + me, PR_GetError(), PR_GetOSError())); 1.325 + goto retry; 1.326 + } 1.327 + if (0 == bytes) 1.328 + { 1.329 + TEST_LOG( 1.330 + cltsrv_log_file, TEST_LOG_ERROR, 1.331 + ("\t\tClient(0x%p): unexpected end of stream\n", 1.332 + PR_GetCurrentThread())); 1.333 + break; 1.334 + } 1.335 + filebytes += bytes; 1.336 + } 1.337 + 1.338 + rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH); 1.339 + if (Aborted(rv)) goto aborted; 1.340 + TEST_ASSERT(PR_SUCCESS == rv); 1.341 +retry: 1.342 + (void)PR_Close(fd); fd = NULL; 1.343 + TEST_LOG( 1.344 + cltsrv_log_file, TEST_LOG_INFO, 1.345 + ("\tClient(0x%p): disconnected from server\n", me)); 1.346 + 1.347 + PR_Lock(client->ml); 1.348 + client->operations += 1; 1.349 + client->bytesTransferred += 2 * descbytes; 1.350 + rv = PR_WaitCondVar(client->stateChange, rand() % clipping); 1.351 + PR_Unlock(client->ml); 1.352 + if (Aborted(rv)) break; 1.353 + } 1.354 + 1.355 +aborted: 1.356 + client->stopped = PR_IntervalNow(); 1.357 + 1.358 + PR_ClearInterrupt(); 1.359 + if (NULL != fd) rv = PR_Close(fd); 1.360 + 1.361 + PR_Lock(client->ml); 1.362 + client->state = cs_exit; 1.363 + PR_NotifyCondVar(client->stateChange); 1.364 + PR_Unlock(client->ml); 1.365 + PR_DELETE(descriptor); 1.366 + TEST_LOG( 1.367 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.368 + ("\tClient(0x%p): stopped after %u operations and %u bytes\n", 1.369 + PR_GetCurrentThread(), client->operations, client->bytesTransferred)); 1.370 + 1.371 +} /* Client */ 1.372 + 1.373 +static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server) 1.374 +{ 1.375 + PRStatus drv, rv; 1.376 + char buffer[1024]; 1.377 + PRFileDesc *file = NULL; 1.378 + PRThread * me = PR_GetCurrentThread(); 1.379 + PRInt32 bytes, descbytes, netbytes, filebytes = 0; 1.380 + CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); 1.381 + PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT); 1.382 + 1.383 + TEST_LOG( 1.384 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.385 + ("\tProcessRequest(0x%p): receiving desciptor\n", me)); 1.386 + bytes = PR_Recv( 1.387 + fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout); 1.388 + if (-1 == bytes) 1.389 + { 1.390 + rv = PR_FAILURE; 1.391 + if (Aborted(rv)) goto exit; 1.392 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.393 + { 1.394 + TEST_LOG( 1.395 + cltsrv_log_file, TEST_LOG_ERROR, 1.396 + ("\tProcessRequest(0x%p): receive timeout\n", me)); 1.397 + } 1.398 + goto exit; 1.399 + } 1.400 + if (0 == bytes) 1.401 + { 1.402 + rv = PR_FAILURE; 1.403 + TEST_LOG( 1.404 + cltsrv_log_file, TEST_LOG_ERROR, 1.405 + ("\tProcessRequest(0x%p): unexpected end of file\n", me)); 1.406 + goto exit; 1.407 + } 1.408 + descbytes = PR_ntohl(descriptor->size); 1.409 + TEST_ASSERT(sizeof(*descriptor) == bytes); 1.410 + 1.411 + TEST_LOG( 1.412 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.413 + ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n", 1.414 + me, descbytes, descriptor->filename)); 1.415 + 1.416 + file = PR_Open( 1.417 + descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666); 1.418 + if (NULL == file) 1.419 + { 1.420 + rv = PR_FAILURE; 1.421 + if (Aborted(rv)) goto aborted; 1.422 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.423 + { 1.424 + TEST_LOG( 1.425 + cltsrv_log_file, TEST_LOG_ERROR, 1.426 + ("\tProcessRequest(0x%p): open file timeout\n", me)); 1.427 + goto aborted; 1.428 + } 1.429 + } 1.430 + TEST_ASSERT(NULL != file); 1.431 + 1.432 + filebytes = 0; 1.433 + while (filebytes < descbytes) 1.434 + { 1.435 + netbytes = sizeof(buffer); 1.436 + if ((descbytes - filebytes) < netbytes) 1.437 + netbytes = descbytes - filebytes; 1.438 + TEST_LOG( 1.439 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.440 + ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes)); 1.441 + bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); 1.442 + if (-1 == bytes) 1.443 + { 1.444 + rv = PR_FAILURE; 1.445 + if (Aborted(rv)) goto aborted; 1.446 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.447 + { 1.448 + TEST_LOG( 1.449 + cltsrv_log_file, TEST_LOG_ERROR, 1.450 + ("\t\tProcessRequest(0x%p): receive data timeout\n", me)); 1.451 + goto aborted; 1.452 + } 1.453 + /* 1.454 + * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED) 1.455 + * on NT here. This is equivalent to ECONNRESET on Unix. 1.456 + * -wtc 1.457 + */ 1.458 + TEST_LOG( 1.459 + cltsrv_log_file, TEST_LOG_WARNING, 1.460 + ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n", 1.461 + me, PR_GetError(), PR_GetOSError())); 1.462 + goto aborted; 1.463 + } 1.464 + if(0 == bytes) 1.465 + { 1.466 + TEST_LOG( 1.467 + cltsrv_log_file, TEST_LOG_WARNING, 1.468 + ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me)); 1.469 + rv = PR_FAILURE; 1.470 + goto aborted; 1.471 + } 1.472 + filebytes += bytes; 1.473 + netbytes = bytes; 1.474 + /* The byte count for PR_Write should be positive */ 1.475 + MY_ASSERT(netbytes > 0); 1.476 + TEST_LOG( 1.477 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.478 + ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes)); 1.479 + bytes = PR_Write(file, buffer, netbytes); 1.480 + if (netbytes != bytes) 1.481 + { 1.482 + rv = PR_FAILURE; 1.483 + if (Aborted(rv)) goto aborted; 1.484 + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) 1.485 + { 1.486 + TEST_LOG( 1.487 + cltsrv_log_file, TEST_LOG_ERROR, 1.488 + ("\t\tProcessRequest(0x%p): write file timeout\n", me)); 1.489 + goto aborted; 1.490 + } 1.491 + } 1.492 + TEST_ASSERT(bytes > 0); 1.493 + } 1.494 + 1.495 + PR_Lock(server->ml); 1.496 + server->operations += 1; 1.497 + server->bytesTransferred += filebytes; 1.498 + PR_Unlock(server->ml); 1.499 + 1.500 + rv = PR_Close(file); file = NULL; 1.501 + if (Aborted(rv)) goto aborted; 1.502 + TEST_ASSERT(PR_SUCCESS == rv); 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); file = NULL; 1.584 + if (Aborted(rv)) goto aborted; 1.585 + TEST_ASSERT(PR_SUCCESS == rv); 1.586 + 1.587 +aborted: 1.588 + PR_ClearInterrupt(); 1.589 + if (NULL != file) PR_Close(file); 1.590 + drv = PR_Delete(descriptor->filename); 1.591 + TEST_ASSERT(PR_SUCCESS == drv); 1.592 +exit: 1.593 + TEST_LOG( 1.594 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.595 + ("\t\tProcessRequest(0x%p): Finished\n", me)); 1.596 + 1.597 + PR_DELETE(descriptor); 1.598 + 1.599 +#if defined(WIN95) 1.600 + PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */ 1.601 +#endif 1.602 + return rv; 1.603 +} /* ProcessRequest */ 1.604 + 1.605 +typedef void (*StartFn)(void*); 1.606 +typedef struct StartObject 1.607 +{ 1.608 + StartFn start; 1.609 + void *arg; 1.610 +} StartObject; 1.611 + 1.612 +#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) 1.613 +#include "md/_pth.h" 1.614 +#include <pthread.h> 1.615 + 1.616 +static void *pthread_start(void *arg) 1.617 +{ 1.618 + StartObject *so = (StartObject*)arg; 1.619 + StartFn start = so->start; 1.620 + void *data = so->arg; 1.621 + PR_Free(so); 1.622 + start(data); 1.623 + return NULL; 1.624 +} /* pthread_start */ 1.625 +#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */ 1.626 + 1.627 +#if defined(IRIX) && !defined(_PR_PTHREADS) 1.628 +#include <sys/types.h> 1.629 +#include <sys/prctl.h> 1.630 +static void sproc_start(void *arg, PRSize size) 1.631 +{ 1.632 + StartObject *so = (StartObject*)arg; 1.633 + StartFn start = so->start; 1.634 + void *data = so->arg; 1.635 + PR_Free(so); 1.636 + start(data); 1.637 +} /* sproc_start */ 1.638 +#endif /* defined(IRIX) && !defined(_PR_PTHREADS) */ 1.639 + 1.640 +#if defined(WIN32) 1.641 +#include <process.h> /* for _beginthreadex() */ 1.642 + 1.643 +static PRUintn __stdcall windows_start(void *arg) 1.644 +{ 1.645 + StartObject *so = (StartObject*)arg; 1.646 + StartFn start = so->start; 1.647 + void *data = so->arg; 1.648 + PR_Free(so); 1.649 + start(data); 1.650 + return 0; 1.651 +} /* windows_start */ 1.652 +#endif /* defined(WIN32) */ 1.653 + 1.654 +static PRStatus JoinThread(PRThread *thread) 1.655 +{ 1.656 + PRStatus rv; 1.657 + switch (thread_provider) 1.658 + { 1.659 + case thread_nspr: 1.660 + rv = PR_JoinThread(thread); 1.661 + break; 1.662 + case thread_pthread: 1.663 +#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) 1.664 + rv = PR_SUCCESS; 1.665 + break; 1.666 +#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */ 1.667 + case thread_win32: 1.668 +#if defined(WIN32) 1.669 + rv = PR_SUCCESS; 1.670 + break; 1.671 +#endif 1.672 + default: 1.673 + rv = PR_FAILURE; 1.674 + break; 1.675 + } 1.676 + return rv; 1.677 +} /* JoinThread */ 1.678 + 1.679 +static PRStatus NewThread( 1.680 + StartFn start, void *arg, PRThreadPriority prio, PRThreadState state) 1.681 +{ 1.682 + PRStatus rv; 1.683 + 1.684 + switch (thread_provider) 1.685 + { 1.686 + case thread_nspr: 1.687 + { 1.688 + PRThread *thread = PR_CreateThread( 1.689 + PR_USER_THREAD, start, arg, 1.690 + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, 1.691 + PR_JOINABLE_THREAD, 0); 1.692 + rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS; 1.693 + } 1.694 + break; 1.695 + case thread_pthread: 1.696 +#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) 1.697 + { 1.698 + int rv; 1.699 + pthread_t id; 1.700 + pthread_attr_t tattr; 1.701 + StartObject *start_object; 1.702 + start_object = PR_NEW(StartObject); 1.703 + PR_ASSERT(NULL != start_object); 1.704 + start_object->start = start; 1.705 + start_object->arg = arg; 1.706 + 1.707 + rv = _PT_PTHREAD_ATTR_INIT(&tattr); 1.708 + PR_ASSERT(0 == rv); 1.709 + 1.710 + rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 1.711 + PR_ASSERT(0 == rv); 1.712 + 1.713 + rv = pthread_attr_setstacksize(&tattr, 64 * 1024); 1.714 + PR_ASSERT(0 == rv); 1.715 + 1.716 + rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object); 1.717 + (void)_PT_PTHREAD_ATTR_DESTROY(&tattr); 1.718 + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; 1.719 + } 1.720 +#else 1.721 + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1.722 + rv = PR_FAILURE; 1.723 +#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */ 1.724 + break; 1.725 + 1.726 + case thread_sproc: 1.727 +#if defined(IRIX) && !defined(_PR_PTHREADS) 1.728 + { 1.729 + PRInt32 pid; 1.730 + StartObject *start_object; 1.731 + start_object = PR_NEW(StartObject); 1.732 + PR_ASSERT(NULL != start_object); 1.733 + start_object->start = start; 1.734 + start_object->arg = arg; 1.735 + pid = sprocsp( 1.736 + sproc_start, PR_SALL, start_object, NULL, 64 * 1024); 1.737 + rv = (0 < pid) ? PR_SUCCESS : PR_FAILURE; 1.738 + } 1.739 +#else 1.740 + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1.741 + rv = PR_FAILURE; 1.742 +#endif /* defined(IRIX) && !defined(_PR_PTHREADS) */ 1.743 + break; 1.744 + case thread_win32: 1.745 +#if defined(WIN32) 1.746 + { 1.747 + void *th; 1.748 + PRUintn id; 1.749 + StartObject *start_object; 1.750 + start_object = PR_NEW(StartObject); 1.751 + PR_ASSERT(NULL != start_object); 1.752 + start_object->start = start; 1.753 + start_object->arg = arg; 1.754 + th = (void*)_beginthreadex( 1.755 + NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */ 1.756 + 0U, /* DWORD - initial thread stack size, in bytes */ 1.757 + windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */ 1.758 + start_object, /* LPVOID - argument for new thread */ 1.759 + STACK_SIZE_PARAM_IS_A_RESERVATION, /*DWORD dwCreationFlags - creation flags */ 1.760 + &id /* LPDWORD - pointer to returned thread identifier */ ); 1.761 + 1.762 + rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS; 1.763 + } 1.764 +#else 1.765 + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1.766 + rv = PR_FAILURE; 1.767 +#endif 1.768 + break; 1.769 + default: 1.770 + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1.771 + rv = PR_FAILURE; 1.772 + } 1.773 + return rv; 1.774 +} /* NewThread */ 1.775 + 1.776 +static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool) 1.777 +{ 1.778 + PRStatus rv; 1.779 + CSWorker_t *worker = PR_NEWZAP(CSWorker_t); 1.780 + worker->server = server; 1.781 + PR_INIT_CLIST(&worker->element); 1.782 + rv = NewThread( 1.783 + Worker, worker, DEFAULT_SERVER_PRIORITY, PR_UNJOINABLE_THREAD); 1.784 + if (PR_FAILURE == rv) PR_DELETE(worker); 1.785 + 1.786 + TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, 1.787 + ("\tCreateWorker(0x%p): create new worker (0x%p)\n", 1.788 + PR_GetCurrentThread(), worker->thread)); 1.789 + 1.790 + return rv; 1.791 +} /* CreateWorker */ 1.792 + 1.793 +static void PR_CALLBACK Worker(void *arg) 1.794 +{ 1.795 + PRStatus rv; 1.796 + PRNetAddr from; 1.797 + PRFileDesc *fd = NULL; 1.798 + CSWorker_t *worker = (CSWorker_t*)arg; 1.799 + CSServer_t *server = worker->server; 1.800 + CSPool_t *pool = &server->pool; 1.801 + 1.802 + PRThread *me = worker->thread = PR_GetCurrentThread(); 1.803 + 1.804 + TEST_LOG( 1.805 + cltsrv_log_file, TEST_LOG_NOTICE, 1.806 + ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1)); 1.807 + 1.808 + PR_Lock(server->ml); 1.809 + PR_APPEND_LINK(&worker->element, &server->list); 1.810 + pool->workers += 1; /* define our existance */ 1.811 + 1.812 + while (cs_run == server->state) 1.813 + { 1.814 + while (pool->accepting >= server->workers.accepting) 1.815 + { 1.816 + TEST_LOG( 1.817 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.818 + ("\t\tWorker(0x%p): waiting for accept slot[%d]\n", 1.819 + me, pool->accepting)); 1.820 + rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT); 1.821 + if (Aborted(rv) || (cs_run != server->state)) 1.822 + { 1.823 + TEST_LOG( 1.824 + cltsrv_log_file, TEST_LOG_NOTICE, 1.825 + ("\tWorker(0x%p): has been %s\n", 1.826 + me, (Aborted(rv) ? "interrupted" : "stopped"))); 1.827 + goto exit; 1.828 + } 1.829 + } 1.830 + pool->accepting += 1; /* how many are really in accept */ 1.831 + PR_Unlock(server->ml); 1.832 + 1.833 + TEST_LOG( 1.834 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.835 + ("\t\tWorker(0x%p): calling accept\n", me)); 1.836 + fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT); 1.837 + 1.838 + PR_Lock(server->ml); 1.839 + pool->accepting -= 1; 1.840 + PR_NotifyCondVar(pool->acceptComplete); 1.841 + 1.842 + if ((NULL == fd) && Aborted(PR_FAILURE)) 1.843 + { 1.844 + if (NULL != server->listener) 1.845 + { 1.846 + PR_Close(server->listener); 1.847 + server->listener = NULL; 1.848 + } 1.849 + goto exit; 1.850 + } 1.851 + 1.852 + if (NULL != fd) 1.853 + { 1.854 + /* 1.855 + ** Create another worker of the total number of workers is 1.856 + ** less than the minimum specified or we have none left in 1.857 + ** accept() AND we're not over the maximum. 1.858 + ** This sort of presumes that the number allowed in accept 1.859 + ** is at least as many as the minimum. Otherwise we'll keep 1.860 + ** creating new threads and deleting them soon after. 1.861 + */ 1.862 + PRBool another = 1.863 + ((pool->workers < server->workers.minimum) || 1.864 + ((0 == pool->accepting) 1.865 + && (pool->workers < server->workers.maximum))) ? 1.866 + PR_TRUE : PR_FALSE; 1.867 + pool->active += 1; 1.868 + PR_Unlock(server->ml); 1.869 + 1.870 + if (another) (void)CreateWorker(server, pool); 1.871 + 1.872 + rv = ProcessRequest(fd, server); 1.873 + if (PR_SUCCESS != rv) 1.874 + TEST_LOG( 1.875 + cltsrv_log_file, TEST_LOG_ERROR, 1.876 + ("\t\tWorker(0x%p): server process ended abnormally\n", me)); 1.877 + (void)PR_Close(fd); fd = NULL; 1.878 + 1.879 + PR_Lock(server->ml); 1.880 + pool->active -= 1; 1.881 + } 1.882 + } 1.883 + 1.884 +exit: 1.885 + PR_ClearInterrupt(); 1.886 + PR_Unlock(server->ml); 1.887 + 1.888 + if (NULL != fd) 1.889 + { 1.890 + (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH); 1.891 + (void)PR_Close(fd); 1.892 + } 1.893 + 1.894 + TEST_LOG( 1.895 + cltsrv_log_file, TEST_LOG_NOTICE, 1.896 + ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers)); 1.897 + 1.898 + PR_Lock(server->ml); 1.899 + pool->workers -= 1; /* undefine our existance */ 1.900 + PR_REMOVE_AND_INIT_LINK(&worker->element); 1.901 + PR_NotifyCondVar(pool->exiting); 1.902 + PR_Unlock(server->ml); 1.903 + 1.904 + PR_DELETE(worker); /* destruction of the "worker" object */ 1.905 + 1.906 +} /* Worker */ 1.907 + 1.908 +static void PR_CALLBACK Server(void *arg) 1.909 +{ 1.910 + PRStatus rv; 1.911 + PRNetAddr serverAddress; 1.912 + CSServer_t *server = (CSServer_t*)arg; 1.913 + PRThread *me = server->thread = PR_GetCurrentThread(); 1.914 + PRSocketOptionData sockOpt; 1.915 + 1.916 + server->listener = PR_Socket(domain, SOCK_STREAM, protocol); 1.917 + 1.918 + sockOpt.option = PR_SockOpt_Reuseaddr; 1.919 + sockOpt.value.reuse_addr = PR_TRUE; 1.920 + rv = PR_SetSocketOption(server->listener, &sockOpt); 1.921 + TEST_ASSERT(PR_SUCCESS == rv); 1.922 + 1.923 + memset(&serverAddress, 0, sizeof(serverAddress)); 1.924 + rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress); 1.925 + 1.926 + rv = PR_Bind(server->listener, &serverAddress); 1.927 + TEST_ASSERT(PR_SUCCESS == rv); 1.928 + 1.929 + rv = PR_Listen(server->listener, server->backlog); 1.930 + TEST_ASSERT(PR_SUCCESS == rv); 1.931 + 1.932 + server->started = PR_IntervalNow(); 1.933 + TimeOfDayMessage("Server started at", me); 1.934 + 1.935 + PR_Lock(server->ml); 1.936 + server->state = cs_run; 1.937 + PR_NotifyCondVar(server->stateChange); 1.938 + PR_Unlock(server->ml); 1.939 + 1.940 + /* 1.941 + ** Create the first worker (actually, a thread that accepts 1.942 + ** connections and then processes the work load as needed). 1.943 + ** From this point on, additional worker threads are created 1.944 + ** as they are needed by existing worker threads. 1.945 + */ 1.946 + rv = CreateWorker(server, &server->pool); 1.947 + TEST_ASSERT(PR_SUCCESS == rv); 1.948 + 1.949 + /* 1.950 + ** From here on this thread is merely hanging around as the contact 1.951 + ** point for the main test driver. It's just waiting for the driver 1.952 + ** to declare the test complete. 1.953 + */ 1.954 + TEST_LOG( 1.955 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.956 + ("\tServer(0x%p): waiting for state change\n", me)); 1.957 + 1.958 + PR_Lock(server->ml); 1.959 + while ((cs_run == server->state) && !Aborted(rv)) 1.960 + { 1.961 + rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); 1.962 + } 1.963 + PR_Unlock(server->ml); 1.964 + PR_ClearInterrupt(); 1.965 + 1.966 + TEST_LOG( 1.967 + cltsrv_log_file, TEST_LOG_INFO, 1.968 + ("\tServer(0x%p): shutting down workers\n", me)); 1.969 + 1.970 + /* 1.971 + ** Get all the worker threads to exit. They know how to 1.972 + ** clean up after themselves, so this is just a matter of 1.973 + ** waiting for clorine in the pool to take effect. During 1.974 + ** this stage we're ignoring interrupts. 1.975 + */ 1.976 + server->workers.minimum = server->workers.maximum = 0; 1.977 + 1.978 + PR_Lock(server->ml); 1.979 + while (!PR_CLIST_IS_EMPTY(&server->list)) 1.980 + { 1.981 + PRCList *head = PR_LIST_HEAD(&server->list); 1.982 + CSWorker_t *worker = (CSWorker_t*)head; 1.983 + TEST_LOG( 1.984 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.985 + ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker)); 1.986 + rv = PR_Interrupt(worker->thread); 1.987 + TEST_ASSERT(PR_SUCCESS == rv); 1.988 + PR_REMOVE_AND_INIT_LINK(head); 1.989 + } 1.990 + 1.991 + while (server->pool.workers > 0) 1.992 + { 1.993 + TEST_LOG( 1.994 + cltsrv_log_file, TEST_LOG_NOTICE, 1.995 + ("\tServer(0x%p): waiting for %u workers to exit\n", 1.996 + me, server->pool.workers)); 1.997 + (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT); 1.998 + } 1.999 + 1.1000 + server->state = cs_exit; 1.1001 + PR_NotifyCondVar(server->stateChange); 1.1002 + PR_Unlock(server->ml); 1.1003 + 1.1004 + TEST_LOG( 1.1005 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.1006 + ("\tServer(0x%p): stopped after %u operations and %u bytes\n", 1.1007 + me, server->operations, server->bytesTransferred)); 1.1008 + 1.1009 + if (NULL != server->listener) PR_Close(server->listener); 1.1010 + server->stopped = PR_IntervalNow(); 1.1011 + 1.1012 +} /* Server */ 1.1013 + 1.1014 +static void WaitForCompletion(PRIntn execution) 1.1015 +{ 1.1016 + while (execution > 0) 1.1017 + { 1.1018 + PRIntn dally = (execution > 30) ? 30 : execution; 1.1019 + PR_Sleep(PR_SecondsToInterval(dally)); 1.1020 + if (pthread_stats) PT_FPrintStats(debug_out, "\nPThread Statistics\n"); 1.1021 + execution -= dally; 1.1022 + } 1.1023 +} /* WaitForCompletion */ 1.1024 + 1.1025 +static void Help(void) 1.1026 +{ 1.1027 + PR_fprintf(debug_out, "cltsrv test program usage:\n"); 1.1028 + PR_fprintf(debug_out, "\t-a <n> threads allowed in accept (5)\n"); 1.1029 + PR_fprintf(debug_out, "\t-b <n> backlock for listen (5)\n"); 1.1030 + PR_fprintf(debug_out, "\t-c <threads> number of clients to create (1)\n"); 1.1031 + PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n"); 1.1032 + PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n"); 1.1033 + PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds (10)\n"); 1.1034 + PR_fprintf(debug_out, "\t-s <string> dsn name of server (localhost)\n"); 1.1035 + PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n"); 1.1036 + PR_fprintf(debug_out, "\t-T <string> thread provider ('n' | 'p' | 'w')(n)\n"); 1.1037 + PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n"); 1.1038 + PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n"); 1.1039 + PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n"); 1.1040 + PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n"); 1.1041 + PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n"); 1.1042 + PR_fprintf(debug_out, "\t-h this message\n"); 1.1043 +} /* Help */ 1.1044 + 1.1045 +static Verbosity IncrementVerbosity(void) 1.1046 +{ 1.1047 + PRIntn verboge = (PRIntn)verbosity + 1; 1.1048 + return (Verbosity)verboge; 1.1049 +} /* IncrementVerbosity */ 1.1050 + 1.1051 +int main(int argc, char **argv) 1.1052 +{ 1.1053 + PRUintn index; 1.1054 + PRBool boolean; 1.1055 + CSClient_t *client; 1.1056 + PRStatus rv, joinStatus; 1.1057 + CSServer_t *server = NULL; 1.1058 + char *thread_type; 1.1059 + 1.1060 + PRUintn backlog = DEFAULT_BACKLOG; 1.1061 + PRUintn clients = DEFAULT_CLIENTS; 1.1062 + const char *serverName = DEFAULT_SERVER; 1.1063 + PRBool serverIsLocal = PR_TRUE; 1.1064 + PRUintn accepting = ALLOWED_IN_ACCEPT; 1.1065 + PRUintn workersMin = DEFAULT_WORKERS_MIN; 1.1066 + PRUintn workersMax = DEFAULT_WORKERS_MAX; 1.1067 + PRIntn execution = DEFAULT_EXECUTION_TIME; 1.1068 + 1.1069 + /* 1.1070 + * -G use global threads 1.1071 + * -a <n> threads allowed in accept 1.1072 + * -b <n> backlock for listen 1.1073 + * -c <threads> number of clients to create 1.1074 + * -w <threads> minimal number of server threads 1.1075 + * -W <threads> maximum number of server threads 1.1076 + * -e <seconds> duration of the test in seconds 1.1077 + * -s <string> dsn name of server (implies no server here) 1.1078 + * -v verbosity 1.1079 + */ 1.1080 + 1.1081 + PLOptStatus os; 1.1082 + PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:w:W:e:s:T:vdhp"); 1.1083 + 1.1084 +#if defined(WIN32) 1.1085 + thread_provider = thread_win32; 1.1086 +#elif defined(_PR_PTHREADS) 1.1087 + thread_provider = thread_pthread; 1.1088 +#elif defined(IRIX) 1.1089 + thread_provider = thread_sproc; 1.1090 +#else 1.1091 + thread_provider = thread_nspr; 1.1092 +#endif 1.1093 + 1.1094 + debug_out = PR_GetSpecialFD(PR_StandardError); 1.1095 + 1.1096 + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 1.1097 + { 1.1098 + if (PL_OPT_BAD == os) continue; 1.1099 + switch (opt->option) 1.1100 + { 1.1101 + case 'G': /* use global threads */ 1.1102 + thread_scope = PR_GLOBAL_THREAD; 1.1103 + break; 1.1104 + case 'X': /* use XTP as transport */ 1.1105 + protocol = 36; 1.1106 + break; 1.1107 + case '6': /* Use IPv6 */ 1.1108 + domain = PR_AF_INET6; 1.1109 + break; 1.1110 + case 'a': /* the value for accepting */ 1.1111 + accepting = atoi(opt->value); 1.1112 + break; 1.1113 + case 'b': /* the value for backlock */ 1.1114 + backlog = atoi(opt->value); 1.1115 + break; 1.1116 + case 'T': /* the thread provider */ 1.1117 + if ('n' == *opt->value) thread_provider = thread_nspr; 1.1118 + else if ('p' == *opt->value) thread_provider = thread_pthread; 1.1119 + else if ('w' == *opt->value) thread_provider = thread_win32; 1.1120 + else {Help(); return 2; } 1.1121 + break; 1.1122 + case 'c': /* number of client threads */ 1.1123 + clients = atoi(opt->value); 1.1124 + break; 1.1125 + case 'w': /* minimum server worker threads */ 1.1126 + workersMin = atoi(opt->value); 1.1127 + break; 1.1128 + case 'W': /* maximum server worker threads */ 1.1129 + workersMax = atoi(opt->value); 1.1130 + break; 1.1131 + case 'e': /* program execution time in seconds */ 1.1132 + execution = atoi(opt->value); 1.1133 + break; 1.1134 + case 's': /* server's address */ 1.1135 + serverName = opt->value; 1.1136 + break; 1.1137 + case 'v': /* verbosity */ 1.1138 + verbosity = IncrementVerbosity(); 1.1139 + break; 1.1140 + case 'd': /* debug mode */ 1.1141 + debug_mode = PR_TRUE; 1.1142 + break; 1.1143 + case 'p': /* pthread mode */ 1.1144 + pthread_stats = PR_TRUE; 1.1145 + break; 1.1146 + case 'h': 1.1147 + default: 1.1148 + Help(); 1.1149 + return 2; 1.1150 + } 1.1151 + } 1.1152 + PL_DestroyOptState(opt); 1.1153 + 1.1154 + if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) serverIsLocal = PR_FALSE; 1.1155 + if (0 == execution) execution = DEFAULT_EXECUTION_TIME; 1.1156 + if (0 == workersMax) workersMax = DEFAULT_WORKERS_MAX; 1.1157 + if (0 == workersMin) workersMin = DEFAULT_WORKERS_MIN; 1.1158 + if (0 == accepting) accepting = ALLOWED_IN_ACCEPT; 1.1159 + if (0 == backlog) backlog = DEFAULT_BACKLOG; 1.1160 + 1.1161 + if (workersMin > accepting) accepting = workersMin; 1.1162 + 1.1163 + PR_STDIO_INIT(); 1.1164 + TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread()); 1.1165 + 1.1166 + cltsrv_log_file = PR_NewLogModule("cltsrv_log"); 1.1167 + MY_ASSERT(NULL != cltsrv_log_file); 1.1168 + boolean = PR_SetLogFile("cltsrv.log"); 1.1169 + MY_ASSERT(boolean); 1.1170 + 1.1171 + if (serverIsLocal) 1.1172 + { 1.1173 + /* Establish the server */ 1.1174 + TEST_LOG( 1.1175 + cltsrv_log_file, TEST_LOG_INFO, 1.1176 + ("main(0x%p): starting server\n", PR_GetCurrentThread())); 1.1177 + 1.1178 + server = PR_NEWZAP(CSServer_t); 1.1179 + PR_INIT_CLIST(&server->list); 1.1180 + server->state = cs_init; 1.1181 + server->ml = PR_NewLock(); 1.1182 + server->backlog = backlog; 1.1183 + server->port = DEFAULT_PORT; 1.1184 + server->workers.minimum = workersMin; 1.1185 + server->workers.maximum = workersMax; 1.1186 + server->workers.accepting = accepting; 1.1187 + server->stateChange = PR_NewCondVar(server->ml); 1.1188 + server->pool.exiting = PR_NewCondVar(server->ml); 1.1189 + server->pool.acceptComplete = PR_NewCondVar(server->ml); 1.1190 + 1.1191 + TEST_LOG( 1.1192 + cltsrv_log_file, TEST_LOG_NOTICE, 1.1193 + ("main(0x%p): creating server thread\n", PR_GetCurrentThread())); 1.1194 + 1.1195 + rv = NewThread( 1.1196 + Server, server, PR_PRIORITY_HIGH, PR_JOINABLE_THREAD); 1.1197 + TEST_ASSERT(PR_SUCCESS == rv); 1.1198 + 1.1199 + TEST_LOG( 1.1200 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.1201 + ("main(0x%p): waiting for server init\n", PR_GetCurrentThread())); 1.1202 + 1.1203 + PR_Lock(server->ml); 1.1204 + while (server->state == cs_init) 1.1205 + PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); 1.1206 + PR_Unlock(server->ml); 1.1207 + 1.1208 + TEST_LOG( 1.1209 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.1210 + ("main(0x%p): server init complete (port #%d)\n", 1.1211 + PR_GetCurrentThread(), server->port)); 1.1212 + } 1.1213 + 1.1214 + if (clients != 0) 1.1215 + { 1.1216 + /* Create all of the clients */ 1.1217 + PRHostEnt host; 1.1218 + char buffer[BUFFER_SIZE]; 1.1219 + client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t)); 1.1220 + 1.1221 + TEST_LOG( 1.1222 + cltsrv_log_file, TEST_LOG_VERBOSE, 1.1223 + ("main(0x%p): creating %d client threads\n", 1.1224 + PR_GetCurrentThread(), clients)); 1.1225 + 1.1226 + if (!serverIsLocal) 1.1227 + { 1.1228 + rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host); 1.1229 + if (PR_SUCCESS != rv) 1.1230 + { 1.1231 + PL_FPrintError(PR_STDERR, "PR_GetHostByName"); 1.1232 + return 2; 1.1233 + } 1.1234 + } 1.1235 + 1.1236 + for (index = 0; index < clients; ++index) 1.1237 + { 1.1238 + client[index].state = cs_init; 1.1239 + client[index].ml = PR_NewLock(); 1.1240 + if (serverIsLocal) 1.1241 + { 1.1242 + (void)PR_InitializeNetAddr( 1.1243 + PR_IpAddrLoopback, DEFAULT_PORT, 1.1244 + &client[index].serverAddress); 1.1245 + } 1.1246 + else 1.1247 + { 1.1248 + (void)PR_EnumerateHostEnt( 1.1249 + 0, &host, DEFAULT_PORT, &client[index].serverAddress); 1.1250 + } 1.1251 + client[index].stateChange = PR_NewCondVar(client[index].ml); 1.1252 + TEST_LOG( 1.1253 + cltsrv_log_file, TEST_LOG_INFO, 1.1254 + ("main(0x%p): creating client threads\n", PR_GetCurrentThread())); 1.1255 + rv = NewThread( 1.1256 + Client, &client[index], PR_PRIORITY_NORMAL, PR_JOINABLE_THREAD); 1.1257 + TEST_ASSERT(PR_SUCCESS == rv); 1.1258 + PR_Lock(client[index].ml); 1.1259 + while (cs_init == client[index].state) 1.1260 + PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); 1.1261 + PR_Unlock(client[index].ml); 1.1262 + } 1.1263 + } 1.1264 + 1.1265 + /* Then just let them go at it for a bit */ 1.1266 + TEST_LOG( 1.1267 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.1268 + ("main(0x%p): waiting for execution interval (%d seconds)\n", 1.1269 + PR_GetCurrentThread(), execution)); 1.1270 + 1.1271 + WaitForCompletion(execution); 1.1272 + 1.1273 + TimeOfDayMessage("Shutting down", PR_GetCurrentThread()); 1.1274 + 1.1275 + if (clients != 0) 1.1276 + { 1.1277 + for (index = 0; index < clients; ++index) 1.1278 + { 1.1279 + TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, 1.1280 + ("main(0x%p): notifying client(0x%p) to stop\n", 1.1281 + PR_GetCurrentThread(), client[index].thread)); 1.1282 + 1.1283 + PR_Lock(client[index].ml); 1.1284 + if (cs_run == client[index].state) 1.1285 + { 1.1286 + client[index].state = cs_stop; 1.1287 + PR_Interrupt(client[index].thread); 1.1288 + while (cs_stop == client[index].state) 1.1289 + PR_WaitCondVar( 1.1290 + client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); 1.1291 + } 1.1292 + PR_Unlock(client[index].ml); 1.1293 + 1.1294 + TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE, 1.1295 + ("main(0x%p): joining client(0x%p)\n", 1.1296 + PR_GetCurrentThread(), client[index].thread)); 1.1297 + 1.1298 + joinStatus = JoinThread(client[index].thread); 1.1299 + TEST_ASSERT(PR_SUCCESS == joinStatus); 1.1300 + PR_DestroyCondVar(client[index].stateChange); 1.1301 + PR_DestroyLock(client[index].ml); 1.1302 + } 1.1303 + PR_DELETE(client); 1.1304 + } 1.1305 + 1.1306 + if (NULL != server) 1.1307 + { 1.1308 + /* All clients joined - retrieve the server */ 1.1309 + TEST_LOG( 1.1310 + cltsrv_log_file, TEST_LOG_NOTICE, 1.1311 + ("main(0x%p): notifying server(0x%p) to stop\n", 1.1312 + PR_GetCurrentThread(), server->thread)); 1.1313 + 1.1314 + PR_Lock(server->ml); 1.1315 + server->state = cs_stop; 1.1316 + PR_Interrupt(server->thread); 1.1317 + while (cs_exit != server->state) 1.1318 + PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); 1.1319 + PR_Unlock(server->ml); 1.1320 + 1.1321 + TEST_LOG( 1.1322 + cltsrv_log_file, TEST_LOG_NOTICE, 1.1323 + ("main(0x%p): joining server(0x%p)\n", 1.1324 + PR_GetCurrentThread(), server->thread)); 1.1325 + joinStatus = JoinThread(server->thread); 1.1326 + TEST_ASSERT(PR_SUCCESS == joinStatus); 1.1327 + 1.1328 + PR_DestroyCondVar(server->stateChange); 1.1329 + PR_DestroyCondVar(server->pool.exiting); 1.1330 + PR_DestroyCondVar(server->pool.acceptComplete); 1.1331 + PR_DestroyLock(server->ml); 1.1332 + PR_DELETE(server); 1.1333 + } 1.1334 + 1.1335 + TEST_LOG( 1.1336 + cltsrv_log_file, TEST_LOG_ALWAYS, 1.1337 + ("main(0x%p): test complete\n", PR_GetCurrentThread())); 1.1338 + 1.1339 + if (thread_provider == thread_win32) 1.1340 + thread_type = "\nWin32 Thread Statistics\n"; 1.1341 + else if (thread_provider == thread_pthread) 1.1342 + thread_type = "\npthread Statistics\n"; 1.1343 + else if (thread_provider == thread_sproc) 1.1344 + thread_type = "\nsproc Statistics\n"; 1.1345 + else { 1.1346 + PR_ASSERT(thread_provider == thread_nspr); 1.1347 + thread_type = "\nPRThread Statistics\nn"; 1.1348 + } 1.1349 + 1.1350 + PT_FPrintStats(debug_out, thread_type); 1.1351 + 1.1352 + TimeOfDayMessage("Test exiting at", PR_GetCurrentThread()); 1.1353 + PR_Cleanup(); 1.1354 + return 0; 1.1355 +} /* main */ 1.1356 + 1.1357 +/* cltsrv.c */