nsprpub/pr/tests/cltsrv.c

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

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

mercurial