nsprpub/pr/tests/provider.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 BUFFER_SIZE 1024
michael@0 64 #define DEFAULT_BACKLOG 5
michael@0 65 #define DEFAULT_PORT 13000
michael@0 66 #define DEFAULT_CLIENTS 1
michael@0 67 #define ALLOWED_IN_ACCEPT 1
michael@0 68 #define DEFAULT_CLIPPING 1000
michael@0 69 #define DEFAULT_WORKERS_MIN 1
michael@0 70 #define DEFAULT_WORKERS_MAX 1
michael@0 71 #define DEFAULT_SERVER "localhost"
michael@0 72 #define DEFAULT_EXECUTION_TIME 10
michael@0 73 #define DEFAULT_CLIENT_TIMEOUT 4000
michael@0 74 #define DEFAULT_SERVER_TIMEOUT 4000
michael@0 75 #define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH
michael@0 76
michael@0 77 typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t;
michael@0 78
michael@0 79 static void PR_CALLBACK Worker(void *arg);
michael@0 80 typedef struct CSPool_s CSPool_t;
michael@0 81 typedef struct CSWorker_s CSWorker_t;
michael@0 82 typedef struct CSServer_s CSServer_t;
michael@0 83 typedef enum Verbosity
michael@0 84 {
michael@0 85 TEST_LOG_ALWAYS,
michael@0 86 TEST_LOG_ERROR,
michael@0 87 TEST_LOG_WARNING,
michael@0 88 TEST_LOG_NOTICE,
michael@0 89 TEST_LOG_INFO,
michael@0 90 TEST_LOG_STATUS,
michael@0 91 TEST_LOG_VERBOSE
michael@0 92 } Verbosity;
michael@0 93
michael@0 94 static enum {
michael@0 95 thread_nspr, thread_pthread, thread_sproc, thread_win32
michael@0 96 } thread_provider;
michael@0 97
michael@0 98 static PRInt32 domain = AF_INET;
michael@0 99 static PRInt32 protocol = 6; /* TCP */
michael@0 100 static PRFileDesc *debug_out = NULL;
michael@0 101 static PRBool debug_mode = PR_FALSE;
michael@0 102 static PRBool pthread_stats = PR_FALSE;
michael@0 103 static Verbosity verbosity = TEST_LOG_ALWAYS;
michael@0 104 static PRThreadScope thread_scope = PR_LOCAL_THREAD;
michael@0 105
michael@0 106 struct CSWorker_s
michael@0 107 {
michael@0 108 PRCList element; /* list of the server's workers */
michael@0 109
michael@0 110 PRThread *thread; /* this worker objects thread */
michael@0 111 CSServer_t *server; /* back pointer to server structure */
michael@0 112 };
michael@0 113
michael@0 114 struct CSPool_s
michael@0 115 {
michael@0 116 PRCondVar *exiting;
michael@0 117 PRCondVar *acceptComplete;
michael@0 118 PRUint32 accepting, active, workers;
michael@0 119 };
michael@0 120
michael@0 121 struct CSServer_s
michael@0 122 {
michael@0 123 PRCList list; /* head of worker list */
michael@0 124
michael@0 125 PRLock *ml;
michael@0 126 PRThread *thread; /* the main server thread */
michael@0 127 PRCondVar *stateChange;
michael@0 128
michael@0 129 PRUint16 port; /* port we're listening on */
michael@0 130 PRUint32 backlog; /* size of our listener backlog */
michael@0 131 PRFileDesc *listener; /* the fd accepting connections */
michael@0 132
michael@0 133 CSPool_t pool; /* statistics on worker threads */
michael@0 134 CSState_t state; /* the server's state */
michael@0 135 struct /* controlling worker counts */
michael@0 136 {
michael@0 137 PRUint32 minimum, maximum, accepting;
michael@0 138 } workers;
michael@0 139
michael@0 140 /* statistics */
michael@0 141 PRIntervalTime started, stopped;
michael@0 142 PRUint32 operations, bytesTransferred;
michael@0 143 };
michael@0 144
michael@0 145 typedef struct CSDescriptor_s
michael@0 146 {
michael@0 147 PRInt32 size; /* size of transfer */
michael@0 148 char filename[60]; /* filename, null padded */
michael@0 149 } CSDescriptor_t;
michael@0 150
michael@0 151 typedef struct CSClient_s
michael@0 152 {
michael@0 153 PRLock *ml;
michael@0 154 PRThread *thread;
michael@0 155 PRCondVar *stateChange;
michael@0 156 PRNetAddr serverAddress;
michael@0 157
michael@0 158 CSState_t state;
michael@0 159
michael@0 160 /* statistics */
michael@0 161 PRIntervalTime started, stopped;
michael@0 162 PRUint32 operations, bytesTransferred;
michael@0 163 } CSClient_t;
michael@0 164
michael@0 165 #define TEST_LOG(l, p, a) \
michael@0 166 do { \
michael@0 167 if (debug_mode || (p <= verbosity)) printf a; \
michael@0 168 } while (0)
michael@0 169
michael@0 170 PRLogModuleInfo *cltsrv_log_file = NULL;
michael@0 171
michael@0 172 #define MY_ASSERT(_expr) \
michael@0 173 ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
michael@0 174
michael@0 175 #define TEST_ASSERT(_expr) \
michael@0 176 ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
michael@0 177
michael@0 178 static void _MY_Assert(const char *s, const char *file, PRIntn ln)
michael@0 179 {
michael@0 180 PL_PrintError(NULL);
michael@0 181 PR_Assert(s, file, ln);
michael@0 182 } /* _MY_Assert */
michael@0 183
michael@0 184 static PRBool Aborted(PRStatus rv)
michael@0 185 {
michael@0 186 return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ?
michael@0 187 PR_TRUE : PR_FALSE;
michael@0 188 }
michael@0 189
michael@0 190 static void TimeOfDayMessage(const char *msg, PRThread* me)
michael@0 191 {
michael@0 192 char buffer[100];
michael@0 193 PRExplodedTime tod;
michael@0 194 PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod);
michael@0 195 (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod);
michael@0 196
michael@0 197 TEST_LOG(
michael@0 198 cltsrv_log_file, TEST_LOG_ALWAYS,
michael@0 199 ("%s(0x%p): %s\n", msg, me, buffer));
michael@0 200 } /* TimeOfDayMessage */
michael@0 201
michael@0 202
michael@0 203 static void PR_CALLBACK Client(void *arg)
michael@0 204 {
michael@0 205 PRStatus rv;
michael@0 206 PRIntn index;
michael@0 207 char buffer[1024];
michael@0 208 PRFileDesc *fd = NULL;
michael@0 209 PRUintn clipping = DEFAULT_CLIPPING;
michael@0 210 CSClient_t *client = (CSClient_t*)arg;
michael@0 211 PRThread *me = client->thread = PR_GetCurrentThread();
michael@0 212 CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
michael@0 213 PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT);
michael@0 214
michael@0 215
michael@0 216 for (index = 0; index < sizeof(buffer); ++index)
michael@0 217 buffer[index] = (char)index;
michael@0 218
michael@0 219 client->started = PR_IntervalNow();
michael@0 220
michael@0 221 PR_Lock(client->ml);
michael@0 222 client->state = cs_run;
michael@0 223 PR_NotifyCondVar(client->stateChange);
michael@0 224 PR_Unlock(client->ml);
michael@0 225
michael@0 226 TimeOfDayMessage("Client started at", me);
michael@0 227
michael@0 228 while (cs_run == client->state)
michael@0 229 {
michael@0 230 PRInt32 bytes, descbytes, filebytes, netbytes;
michael@0 231
michael@0 232 (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer));
michael@0 233 TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
michael@0 234 ("\tClient(0x%p): connecting to server at %s\n", me, buffer));
michael@0 235
michael@0 236 fd = PR_Socket(domain, SOCK_STREAM, protocol);
michael@0 237 TEST_ASSERT(NULL != fd);
michael@0 238 rv = PR_Connect(fd, &client->serverAddress, timeout);
michael@0 239 if (PR_FAILURE == rv)
michael@0 240 {
michael@0 241 TEST_LOG(
michael@0 242 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 243 ("\tClient(0x%p): conection failed\n", me));
michael@0 244 goto aborted;
michael@0 245 }
michael@0 246
michael@0 247 memset(descriptor, 0, sizeof(*descriptor));
michael@0 248 descriptor->size = PR_htonl(descbytes = rand() % clipping);
michael@0 249 PR_snprintf(
michael@0 250 descriptor->filename, sizeof(descriptor->filename),
michael@0 251 "CS%p%p-%p.dat", client->started, me, client->operations);
michael@0 252 TEST_LOG(
michael@0 253 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 254 ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes));
michael@0 255 bytes = PR_Send(
michael@0 256 fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout);
michael@0 257 if (sizeof(CSDescriptor_t) != bytes)
michael@0 258 {
michael@0 259 if (Aborted(PR_FAILURE)) goto aborted;
michael@0 260 if (PR_IO_TIMEOUT_ERROR == PR_GetError())
michael@0 261 {
michael@0 262 TEST_LOG(
michael@0 263 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 264 ("\tClient(0x%p): send descriptor timeout\n", me));
michael@0 265 goto retry;
michael@0 266 }
michael@0 267 }
michael@0 268 TEST_ASSERT(sizeof(*descriptor) == bytes);
michael@0 269
michael@0 270 netbytes = 0;
michael@0 271 while (netbytes < descbytes)
michael@0 272 {
michael@0 273 filebytes = sizeof(buffer);
michael@0 274 if ((descbytes - netbytes) < filebytes)
michael@0 275 filebytes = descbytes - netbytes;
michael@0 276 TEST_LOG(
michael@0 277 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 278 ("\tClient(0x%p): sending %d bytes\n", me, filebytes));
michael@0 279 bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
michael@0 280 if (filebytes != bytes)
michael@0 281 {
michael@0 282 if (Aborted(PR_FAILURE)) goto aborted;
michael@0 283 if (PR_IO_TIMEOUT_ERROR == PR_GetError())
michael@0 284 {
michael@0 285 TEST_LOG(
michael@0 286 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 287 ("\tClient(0x%p): send data timeout\n", me));
michael@0 288 goto retry;
michael@0 289 }
michael@0 290 }
michael@0 291 TEST_ASSERT(bytes == filebytes);
michael@0 292 netbytes += bytes;
michael@0 293 }
michael@0 294 filebytes = 0;
michael@0 295 while (filebytes < descbytes)
michael@0 296 {
michael@0 297 netbytes = sizeof(buffer);
michael@0 298 if ((descbytes - filebytes) < netbytes)
michael@0 299 netbytes = descbytes - filebytes;
michael@0 300 TEST_LOG(
michael@0 301 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 302 ("\tClient(0x%p): receiving %d bytes\n", me, netbytes));
michael@0 303 bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
michael@0 304 if (-1 == bytes)
michael@0 305 {
michael@0 306 if (Aborted(PR_FAILURE))
michael@0 307 {
michael@0 308 TEST_LOG(
michael@0 309 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 310 ("\tClient(0x%p): receive data aborted\n", me));
michael@0 311 goto aborted;
michael@0 312 }
michael@0 313 else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
michael@0 314 TEST_LOG(
michael@0 315 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 316 ("\tClient(0x%p): receive data timeout\n", me));
michael@0 317 else
michael@0 318 TEST_LOG(
michael@0 319 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 320 ("\tClient(0x%p): receive error (%d, %d)\n",
michael@0 321 me, PR_GetError(), PR_GetOSError()));
michael@0 322 goto retry;
michael@0 323 }
michael@0 324 if (0 == bytes)
michael@0 325 {
michael@0 326 TEST_LOG(
michael@0 327 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 328 ("\t\tClient(0x%p): unexpected end of stream\n",
michael@0 329 PR_GetCurrentThread()));
michael@0 330 break;
michael@0 331 }
michael@0 332 filebytes += bytes;
michael@0 333 }
michael@0 334
michael@0 335 rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
michael@0 336 if (Aborted(rv)) goto aborted;
michael@0 337 TEST_ASSERT(PR_SUCCESS == rv);
michael@0 338 retry:
michael@0 339 (void)PR_Close(fd); fd = NULL;
michael@0 340 TEST_LOG(
michael@0 341 cltsrv_log_file, TEST_LOG_INFO,
michael@0 342 ("\tClient(0x%p): disconnected from server\n", me));
michael@0 343
michael@0 344 PR_Lock(client->ml);
michael@0 345 client->operations += 1;
michael@0 346 client->bytesTransferred += 2 * descbytes;
michael@0 347 rv = PR_WaitCondVar(client->stateChange, rand() % clipping);
michael@0 348 PR_Unlock(client->ml);
michael@0 349 if (Aborted(rv)) break;
michael@0 350 }
michael@0 351
michael@0 352 aborted:
michael@0 353 client->stopped = PR_IntervalNow();
michael@0 354
michael@0 355 PR_ClearInterrupt();
michael@0 356 if (NULL != fd) rv = PR_Close(fd);
michael@0 357
michael@0 358 PR_Lock(client->ml);
michael@0 359 client->state = cs_exit;
michael@0 360 PR_NotifyCondVar(client->stateChange);
michael@0 361 PR_Unlock(client->ml);
michael@0 362 PR_DELETE(descriptor);
michael@0 363 TEST_LOG(
michael@0 364 cltsrv_log_file, TEST_LOG_ALWAYS,
michael@0 365 ("\tClient(0x%p): stopped after %u operations and %u bytes\n",
michael@0 366 PR_GetCurrentThread(), client->operations, client->bytesTransferred));
michael@0 367
michael@0 368 } /* Client */
michael@0 369
michael@0 370 static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server)
michael@0 371 {
michael@0 372 PRStatus drv, rv;
michael@0 373 char buffer[1024];
michael@0 374 PRFileDesc *file = NULL;
michael@0 375 PRThread * me = PR_GetCurrentThread();
michael@0 376 PRInt32 bytes, descbytes, netbytes, filebytes = 0;
michael@0 377 CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
michael@0 378 PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT);
michael@0 379
michael@0 380 TEST_LOG(
michael@0 381 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 382 ("\tProcessRequest(0x%p): receiving desciptor\n", me));
michael@0 383 bytes = PR_Recv(
michael@0 384 fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout);
michael@0 385 if (-1 == bytes)
michael@0 386 {
michael@0 387 rv = PR_FAILURE;
michael@0 388 if (Aborted(rv)) goto exit;
michael@0 389 if (PR_IO_TIMEOUT_ERROR == PR_GetError())
michael@0 390 {
michael@0 391 TEST_LOG(
michael@0 392 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 393 ("\tProcessRequest(0x%p): receive timeout\n", me));
michael@0 394 }
michael@0 395 goto exit;
michael@0 396 }
michael@0 397 if (0 == bytes)
michael@0 398 {
michael@0 399 rv = PR_FAILURE;
michael@0 400 TEST_LOG(
michael@0 401 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 402 ("\tProcessRequest(0x%p): unexpected end of file\n", me));
michael@0 403 goto exit;
michael@0 404 }
michael@0 405 descbytes = PR_ntohl(descriptor->size);
michael@0 406 TEST_ASSERT(sizeof(*descriptor) == bytes);
michael@0 407
michael@0 408 TEST_LOG(
michael@0 409 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 410 ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n",
michael@0 411 me, descbytes, descriptor->filename));
michael@0 412
michael@0 413 file = PR_Open(
michael@0 414 descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666);
michael@0 415 if (NULL == file)
michael@0 416 {
michael@0 417 rv = PR_FAILURE;
michael@0 418 if (Aborted(rv)) goto aborted;
michael@0 419 if (PR_IO_TIMEOUT_ERROR == PR_GetError())
michael@0 420 {
michael@0 421 TEST_LOG(
michael@0 422 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 423 ("\tProcessRequest(0x%p): open file timeout\n", me));
michael@0 424 goto aborted;
michael@0 425 }
michael@0 426 }
michael@0 427 TEST_ASSERT(NULL != file);
michael@0 428
michael@0 429 filebytes = 0;
michael@0 430 while (filebytes < descbytes)
michael@0 431 {
michael@0 432 netbytes = sizeof(buffer);
michael@0 433 if ((descbytes - filebytes) < netbytes)
michael@0 434 netbytes = descbytes - filebytes;
michael@0 435 TEST_LOG(
michael@0 436 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 437 ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes));
michael@0 438 bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
michael@0 439 if (-1 == bytes)
michael@0 440 {
michael@0 441 rv = PR_FAILURE;
michael@0 442 if (Aborted(rv)) goto aborted;
michael@0 443 if (PR_IO_TIMEOUT_ERROR == PR_GetError())
michael@0 444 {
michael@0 445 TEST_LOG(
michael@0 446 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 447 ("\t\tProcessRequest(0x%p): receive data timeout\n", me));
michael@0 448 goto aborted;
michael@0 449 }
michael@0 450 /*
michael@0 451 * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
michael@0 452 * on NT here. This is equivalent to ECONNRESET on Unix.
michael@0 453 * -wtc
michael@0 454 */
michael@0 455 TEST_LOG(
michael@0 456 cltsrv_log_file, TEST_LOG_WARNING,
michael@0 457 ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n",
michael@0 458 me, PR_GetError(), PR_GetOSError()));
michael@0 459 goto aborted;
michael@0 460 }
michael@0 461 if(0 == bytes)
michael@0 462 {
michael@0 463 TEST_LOG(
michael@0 464 cltsrv_log_file, TEST_LOG_WARNING,
michael@0 465 ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
michael@0 466 rv = PR_FAILURE;
michael@0 467 goto aborted;
michael@0 468 }
michael@0 469 filebytes += bytes;
michael@0 470 netbytes = bytes;
michael@0 471 /* The byte count for PR_Write should be positive */
michael@0 472 MY_ASSERT(netbytes > 0);
michael@0 473 TEST_LOG(
michael@0 474 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 475 ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes));
michael@0 476 bytes = PR_Write(file, buffer, netbytes);
michael@0 477 if (netbytes != bytes)
michael@0 478 {
michael@0 479 rv = PR_FAILURE;
michael@0 480 if (Aborted(rv)) goto aborted;
michael@0 481 if (PR_IO_TIMEOUT_ERROR == PR_GetError())
michael@0 482 {
michael@0 483 TEST_LOG(
michael@0 484 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 485 ("\t\tProcessRequest(0x%p): write file timeout\n", me));
michael@0 486 goto aborted;
michael@0 487 }
michael@0 488 }
michael@0 489 TEST_ASSERT(bytes > 0);
michael@0 490 }
michael@0 491
michael@0 492 PR_Lock(server->ml);
michael@0 493 server->operations += 1;
michael@0 494 server->bytesTransferred += filebytes;
michael@0 495 PR_Unlock(server->ml);
michael@0 496
michael@0 497 rv = PR_Close(file); file = NULL;
michael@0 498 if (Aborted(rv)) goto aborted;
michael@0 499 TEST_ASSERT(PR_SUCCESS == rv);
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); file = NULL;
michael@0 581 if (Aborted(rv)) goto aborted;
michael@0 582 TEST_ASSERT(PR_SUCCESS == rv);
michael@0 583
michael@0 584 aborted:
michael@0 585 PR_ClearInterrupt();
michael@0 586 if (NULL != file) PR_Close(file);
michael@0 587 drv = PR_Delete(descriptor->filename);
michael@0 588 TEST_ASSERT(PR_SUCCESS == drv);
michael@0 589 exit:
michael@0 590 TEST_LOG(
michael@0 591 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 592 ("\t\tProcessRequest(0x%p): Finished\n", me));
michael@0 593
michael@0 594 PR_DELETE(descriptor);
michael@0 595
michael@0 596 #if defined(WIN95)
michael@0 597 PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */
michael@0 598 #endif
michael@0 599 return rv;
michael@0 600 } /* ProcessRequest */
michael@0 601
michael@0 602 typedef void (*StartFn)(void*);
michael@0 603 typedef struct StartObject
michael@0 604 {
michael@0 605 StartFn start;
michael@0 606 void *arg;
michael@0 607 } StartObject;
michael@0 608
michael@0 609 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
michael@0 610 #include "md/_pth.h"
michael@0 611 #include <pthread.h>
michael@0 612
michael@0 613 static void *pthread_start(void *arg)
michael@0 614 {
michael@0 615 StartObject *so = (StartObject*)arg;
michael@0 616 StartFn start = so->start;
michael@0 617 void *data = so->arg;
michael@0 618 PR_Free(so);
michael@0 619 start(data);
michael@0 620 return NULL;
michael@0 621 } /* pthread_start */
michael@0 622 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
michael@0 623
michael@0 624 #if defined(IRIX) && !defined(_PR_PTHREADS)
michael@0 625 #include <sys/types.h>
michael@0 626 #include <sys/prctl.h>
michael@0 627 static void sproc_start(void *arg, PRSize size)
michael@0 628 {
michael@0 629 StartObject *so = (StartObject*)arg;
michael@0 630 StartFn start = so->start;
michael@0 631 void *data = so->arg;
michael@0 632 PR_Free(so);
michael@0 633 start(data);
michael@0 634 } /* sproc_start */
michael@0 635 #endif /* defined(IRIX) && !defined(_PR_PTHREADS) */
michael@0 636
michael@0 637 #if defined(WIN32)
michael@0 638 #include <process.h> /* for _beginthreadex() */
michael@0 639
michael@0 640 static PRUintn __stdcall windows_start(void *arg)
michael@0 641 {
michael@0 642 StartObject *so = (StartObject*)arg;
michael@0 643 StartFn start = so->start;
michael@0 644 void *data = so->arg;
michael@0 645 PR_Free(so);
michael@0 646 start(data);
michael@0 647 return 0;
michael@0 648 } /* windows_start */
michael@0 649 #endif /* defined(WIN32) */
michael@0 650
michael@0 651 static PRStatus JoinThread(PRThread *thread)
michael@0 652 {
michael@0 653 PRStatus rv;
michael@0 654 switch (thread_provider)
michael@0 655 {
michael@0 656 case thread_nspr:
michael@0 657 rv = PR_JoinThread(thread);
michael@0 658 break;
michael@0 659 case thread_pthread:
michael@0 660 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
michael@0 661 rv = PR_SUCCESS;
michael@0 662 break;
michael@0 663 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
michael@0 664 case thread_win32:
michael@0 665 #if defined(WIN32)
michael@0 666 rv = PR_SUCCESS;
michael@0 667 break;
michael@0 668 #endif
michael@0 669 default:
michael@0 670 rv = PR_FAILURE;
michael@0 671 break;
michael@0 672 }
michael@0 673 return rv;
michael@0 674 } /* JoinThread */
michael@0 675
michael@0 676 static PRStatus NewThread(
michael@0 677 StartFn start, void *arg, PRThreadPriority prio, PRThreadState state)
michael@0 678 {
michael@0 679 PRStatus rv;
michael@0 680
michael@0 681 switch (thread_provider)
michael@0 682 {
michael@0 683 case thread_nspr:
michael@0 684 {
michael@0 685 PRThread *thread = PR_CreateThread(
michael@0 686 PR_USER_THREAD, start, arg,
michael@0 687 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
michael@0 688 PR_JOINABLE_THREAD, 0);
michael@0 689 rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS;
michael@0 690 }
michael@0 691 break;
michael@0 692 case thread_pthread:
michael@0 693 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
michael@0 694 {
michael@0 695 int rv;
michael@0 696 pthread_t id;
michael@0 697 pthread_attr_t tattr;
michael@0 698 StartObject *start_object;
michael@0 699 start_object = PR_NEW(StartObject);
michael@0 700 PR_ASSERT(NULL != start_object);
michael@0 701 start_object->start = start;
michael@0 702 start_object->arg = arg;
michael@0 703
michael@0 704 rv = _PT_PTHREAD_ATTR_INIT(&tattr);
michael@0 705 PR_ASSERT(0 == rv);
michael@0 706
michael@0 707 rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
michael@0 708 PR_ASSERT(0 == rv);
michael@0 709
michael@0 710 rv = pthread_attr_setstacksize(&tattr, 64 * 1024);
michael@0 711 PR_ASSERT(0 == rv);
michael@0 712
michael@0 713 rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object);
michael@0 714 (void)_PT_PTHREAD_ATTR_DESTROY(&tattr);
michael@0 715 return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
michael@0 716 }
michael@0 717 #else
michael@0 718 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
michael@0 719 rv = PR_FAILURE;
michael@0 720 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
michael@0 721 break;
michael@0 722
michael@0 723 case thread_sproc:
michael@0 724 #if defined(IRIX) && !defined(_PR_PTHREADS)
michael@0 725 {
michael@0 726 PRInt32 pid;
michael@0 727 StartObject *start_object;
michael@0 728 start_object = PR_NEW(StartObject);
michael@0 729 PR_ASSERT(NULL != start_object);
michael@0 730 start_object->start = start;
michael@0 731 start_object->arg = arg;
michael@0 732 pid = sprocsp(
michael@0 733 sproc_start, PR_SALL, start_object, NULL, 64 * 1024);
michael@0 734 rv = (0 < pid) ? PR_SUCCESS : PR_FAILURE;
michael@0 735 }
michael@0 736 #else
michael@0 737 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
michael@0 738 rv = PR_FAILURE;
michael@0 739 #endif /* defined(IRIX) && !defined(_PR_PTHREADS) */
michael@0 740 break;
michael@0 741 case thread_win32:
michael@0 742 #if defined(WIN32)
michael@0 743 {
michael@0 744 void *th;
michael@0 745 PRUintn id;
michael@0 746 StartObject *start_object;
michael@0 747 start_object = PR_NEW(StartObject);
michael@0 748 PR_ASSERT(NULL != start_object);
michael@0 749 start_object->start = start;
michael@0 750 start_object->arg = arg;
michael@0 751 th = (void*)_beginthreadex(
michael@0 752 NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */
michael@0 753 0U, /* DWORD - initial thread stack size, in bytes */
michael@0 754 windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */
michael@0 755 start_object, /* LPVOID - argument for new thread */
michael@0 756 STACK_SIZE_PARAM_IS_A_RESERVATION, /*DWORD dwCreationFlags - creation flags */
michael@0 757 &id /* LPDWORD - pointer to returned thread identifier */ );
michael@0 758
michael@0 759 rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS;
michael@0 760 }
michael@0 761 #else
michael@0 762 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
michael@0 763 rv = PR_FAILURE;
michael@0 764 #endif
michael@0 765 break;
michael@0 766 default:
michael@0 767 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
michael@0 768 rv = PR_FAILURE;
michael@0 769 }
michael@0 770 return rv;
michael@0 771 } /* NewThread */
michael@0 772
michael@0 773 static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool)
michael@0 774 {
michael@0 775 PRStatus rv;
michael@0 776 CSWorker_t *worker = PR_NEWZAP(CSWorker_t);
michael@0 777 worker->server = server;
michael@0 778 PR_INIT_CLIST(&worker->element);
michael@0 779 rv = NewThread(
michael@0 780 Worker, worker, DEFAULT_SERVER_PRIORITY, PR_UNJOINABLE_THREAD);
michael@0 781 if (PR_FAILURE == rv) PR_DELETE(worker);
michael@0 782
michael@0 783 TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
michael@0 784 ("\tCreateWorker(0x%p): create new worker (0x%p)\n",
michael@0 785 PR_GetCurrentThread(), worker->thread));
michael@0 786
michael@0 787 return rv;
michael@0 788 } /* CreateWorker */
michael@0 789
michael@0 790 static void PR_CALLBACK Worker(void *arg)
michael@0 791 {
michael@0 792 PRStatus rv;
michael@0 793 PRNetAddr from;
michael@0 794 PRFileDesc *fd = NULL;
michael@0 795 CSWorker_t *worker = (CSWorker_t*)arg;
michael@0 796 CSServer_t *server = worker->server;
michael@0 797 CSPool_t *pool = &server->pool;
michael@0 798
michael@0 799 PRThread *me = worker->thread = PR_GetCurrentThread();
michael@0 800
michael@0 801 TEST_LOG(
michael@0 802 cltsrv_log_file, TEST_LOG_NOTICE,
michael@0 803 ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1));
michael@0 804
michael@0 805 PR_Lock(server->ml);
michael@0 806 PR_APPEND_LINK(&worker->element, &server->list);
michael@0 807 pool->workers += 1; /* define our existance */
michael@0 808
michael@0 809 while (cs_run == server->state)
michael@0 810 {
michael@0 811 while (pool->accepting >= server->workers.accepting)
michael@0 812 {
michael@0 813 TEST_LOG(
michael@0 814 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 815 ("\t\tWorker(0x%p): waiting for accept slot[%d]\n",
michael@0 816 me, pool->accepting));
michael@0 817 rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT);
michael@0 818 if (Aborted(rv) || (cs_run != server->state))
michael@0 819 {
michael@0 820 TEST_LOG(
michael@0 821 cltsrv_log_file, TEST_LOG_NOTICE,
michael@0 822 ("\tWorker(0x%p): has been %s\n",
michael@0 823 me, (Aborted(rv) ? "interrupted" : "stopped")));
michael@0 824 goto exit;
michael@0 825 }
michael@0 826 }
michael@0 827 pool->accepting += 1; /* how many are really in accept */
michael@0 828 PR_Unlock(server->ml);
michael@0 829
michael@0 830 TEST_LOG(
michael@0 831 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 832 ("\t\tWorker(0x%p): calling accept\n", me));
michael@0 833 fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT);
michael@0 834
michael@0 835 PR_Lock(server->ml);
michael@0 836 pool->accepting -= 1;
michael@0 837 PR_NotifyCondVar(pool->acceptComplete);
michael@0 838
michael@0 839 if ((NULL == fd) && Aborted(PR_FAILURE))
michael@0 840 {
michael@0 841 if (NULL != server->listener)
michael@0 842 {
michael@0 843 PR_Close(server->listener);
michael@0 844 server->listener = NULL;
michael@0 845 }
michael@0 846 goto exit;
michael@0 847 }
michael@0 848
michael@0 849 if (NULL != fd)
michael@0 850 {
michael@0 851 /*
michael@0 852 ** Create another worker of the total number of workers is
michael@0 853 ** less than the minimum specified or we have none left in
michael@0 854 ** accept() AND we're not over the maximum.
michael@0 855 ** This sort of presumes that the number allowed in accept
michael@0 856 ** is at least as many as the minimum. Otherwise we'll keep
michael@0 857 ** creating new threads and deleting them soon after.
michael@0 858 */
michael@0 859 PRBool another =
michael@0 860 ((pool->workers < server->workers.minimum) ||
michael@0 861 ((0 == pool->accepting)
michael@0 862 && (pool->workers < server->workers.maximum))) ?
michael@0 863 PR_TRUE : PR_FALSE;
michael@0 864 pool->active += 1;
michael@0 865 PR_Unlock(server->ml);
michael@0 866
michael@0 867 if (another) (void)CreateWorker(server, pool);
michael@0 868
michael@0 869 rv = ProcessRequest(fd, server);
michael@0 870 if (PR_SUCCESS != rv)
michael@0 871 TEST_LOG(
michael@0 872 cltsrv_log_file, TEST_LOG_ERROR,
michael@0 873 ("\t\tWorker(0x%p): server process ended abnormally\n", me));
michael@0 874 (void)PR_Close(fd); fd = NULL;
michael@0 875
michael@0 876 PR_Lock(server->ml);
michael@0 877 pool->active -= 1;
michael@0 878 }
michael@0 879 }
michael@0 880
michael@0 881 exit:
michael@0 882 PR_ClearInterrupt();
michael@0 883 PR_Unlock(server->ml);
michael@0 884
michael@0 885 if (NULL != fd)
michael@0 886 {
michael@0 887 (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
michael@0 888 (void)PR_Close(fd);
michael@0 889 }
michael@0 890
michael@0 891 TEST_LOG(
michael@0 892 cltsrv_log_file, TEST_LOG_NOTICE,
michael@0 893 ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers));
michael@0 894
michael@0 895 PR_Lock(server->ml);
michael@0 896 pool->workers -= 1; /* undefine our existance */
michael@0 897 PR_REMOVE_AND_INIT_LINK(&worker->element);
michael@0 898 PR_NotifyCondVar(pool->exiting);
michael@0 899 PR_Unlock(server->ml);
michael@0 900
michael@0 901 PR_DELETE(worker); /* destruction of the "worker" object */
michael@0 902
michael@0 903 } /* Worker */
michael@0 904
michael@0 905 static void PR_CALLBACK Server(void *arg)
michael@0 906 {
michael@0 907 PRStatus rv;
michael@0 908 PRNetAddr serverAddress;
michael@0 909 CSServer_t *server = (CSServer_t*)arg;
michael@0 910 PRThread *me = server->thread = PR_GetCurrentThread();
michael@0 911 PRSocketOptionData sockOpt;
michael@0 912
michael@0 913 server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
michael@0 914
michael@0 915 sockOpt.option = PR_SockOpt_Reuseaddr;
michael@0 916 sockOpt.value.reuse_addr = PR_TRUE;
michael@0 917 rv = PR_SetSocketOption(server->listener, &sockOpt);
michael@0 918 TEST_ASSERT(PR_SUCCESS == rv);
michael@0 919
michael@0 920 memset(&serverAddress, 0, sizeof(serverAddress));
michael@0 921 rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
michael@0 922
michael@0 923 rv = PR_Bind(server->listener, &serverAddress);
michael@0 924 TEST_ASSERT(PR_SUCCESS == rv);
michael@0 925
michael@0 926 rv = PR_Listen(server->listener, server->backlog);
michael@0 927 TEST_ASSERT(PR_SUCCESS == rv);
michael@0 928
michael@0 929 server->started = PR_IntervalNow();
michael@0 930 TimeOfDayMessage("Server started at", me);
michael@0 931
michael@0 932 PR_Lock(server->ml);
michael@0 933 server->state = cs_run;
michael@0 934 PR_NotifyCondVar(server->stateChange);
michael@0 935 PR_Unlock(server->ml);
michael@0 936
michael@0 937 /*
michael@0 938 ** Create the first worker (actually, a thread that accepts
michael@0 939 ** connections and then processes the work load as needed).
michael@0 940 ** From this point on, additional worker threads are created
michael@0 941 ** as they are needed by existing worker threads.
michael@0 942 */
michael@0 943 rv = CreateWorker(server, &server->pool);
michael@0 944 TEST_ASSERT(PR_SUCCESS == rv);
michael@0 945
michael@0 946 /*
michael@0 947 ** From here on this thread is merely hanging around as the contact
michael@0 948 ** point for the main test driver. It's just waiting for the driver
michael@0 949 ** to declare the test complete.
michael@0 950 */
michael@0 951 TEST_LOG(
michael@0 952 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 953 ("\tServer(0x%p): waiting for state change\n", me));
michael@0 954
michael@0 955 PR_Lock(server->ml);
michael@0 956 while ((cs_run == server->state) && !Aborted(rv))
michael@0 957 {
michael@0 958 rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
michael@0 959 }
michael@0 960 PR_Unlock(server->ml);
michael@0 961 PR_ClearInterrupt();
michael@0 962
michael@0 963 TEST_LOG(
michael@0 964 cltsrv_log_file, TEST_LOG_INFO,
michael@0 965 ("\tServer(0x%p): shutting down workers\n", me));
michael@0 966
michael@0 967 /*
michael@0 968 ** Get all the worker threads to exit. They know how to
michael@0 969 ** clean up after themselves, so this is just a matter of
michael@0 970 ** waiting for clorine in the pool to take effect. During
michael@0 971 ** this stage we're ignoring interrupts.
michael@0 972 */
michael@0 973 server->workers.minimum = server->workers.maximum = 0;
michael@0 974
michael@0 975 PR_Lock(server->ml);
michael@0 976 while (!PR_CLIST_IS_EMPTY(&server->list))
michael@0 977 {
michael@0 978 PRCList *head = PR_LIST_HEAD(&server->list);
michael@0 979 CSWorker_t *worker = (CSWorker_t*)head;
michael@0 980 TEST_LOG(
michael@0 981 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 982 ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker));
michael@0 983 rv = PR_Interrupt(worker->thread);
michael@0 984 TEST_ASSERT(PR_SUCCESS == rv);
michael@0 985 PR_REMOVE_AND_INIT_LINK(head);
michael@0 986 }
michael@0 987
michael@0 988 while (server->pool.workers > 0)
michael@0 989 {
michael@0 990 TEST_LOG(
michael@0 991 cltsrv_log_file, TEST_LOG_NOTICE,
michael@0 992 ("\tServer(0x%p): waiting for %u workers to exit\n",
michael@0 993 me, server->pool.workers));
michael@0 994 (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT);
michael@0 995 }
michael@0 996
michael@0 997 server->state = cs_exit;
michael@0 998 PR_NotifyCondVar(server->stateChange);
michael@0 999 PR_Unlock(server->ml);
michael@0 1000
michael@0 1001 TEST_LOG(
michael@0 1002 cltsrv_log_file, TEST_LOG_ALWAYS,
michael@0 1003 ("\tServer(0x%p): stopped after %u operations and %u bytes\n",
michael@0 1004 me, server->operations, server->bytesTransferred));
michael@0 1005
michael@0 1006 if (NULL != server->listener) PR_Close(server->listener);
michael@0 1007 server->stopped = PR_IntervalNow();
michael@0 1008
michael@0 1009 } /* Server */
michael@0 1010
michael@0 1011 static void WaitForCompletion(PRIntn execution)
michael@0 1012 {
michael@0 1013 while (execution > 0)
michael@0 1014 {
michael@0 1015 PRIntn dally = (execution > 30) ? 30 : execution;
michael@0 1016 PR_Sleep(PR_SecondsToInterval(dally));
michael@0 1017 if (pthread_stats) PT_FPrintStats(debug_out, "\nPThread Statistics\n");
michael@0 1018 execution -= dally;
michael@0 1019 }
michael@0 1020 } /* WaitForCompletion */
michael@0 1021
michael@0 1022 static void Help(void)
michael@0 1023 {
michael@0 1024 PR_fprintf(debug_out, "cltsrv test program usage:\n");
michael@0 1025 PR_fprintf(debug_out, "\t-a <n> threads allowed in accept (5)\n");
michael@0 1026 PR_fprintf(debug_out, "\t-b <n> backlock for listen (5)\n");
michael@0 1027 PR_fprintf(debug_out, "\t-c <threads> number of clients to create (1)\n");
michael@0 1028 PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n");
michael@0 1029 PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n");
michael@0 1030 PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds (10)\n");
michael@0 1031 PR_fprintf(debug_out, "\t-s <string> dsn name of server (localhost)\n");
michael@0 1032 PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n");
michael@0 1033 PR_fprintf(debug_out, "\t-T <string> thread provider ('n' | 'p' | 'w')(n)\n");
michael@0 1034 PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n");
michael@0 1035 PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n");
michael@0 1036 PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n");
michael@0 1037 PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n");
michael@0 1038 PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n");
michael@0 1039 PR_fprintf(debug_out, "\t-h this message\n");
michael@0 1040 } /* Help */
michael@0 1041
michael@0 1042 static Verbosity IncrementVerbosity(void)
michael@0 1043 {
michael@0 1044 PRIntn verboge = (PRIntn)verbosity + 1;
michael@0 1045 return (Verbosity)verboge;
michael@0 1046 } /* IncrementVerbosity */
michael@0 1047
michael@0 1048 int main(int argc, char **argv)
michael@0 1049 {
michael@0 1050 PRUintn index;
michael@0 1051 PRBool boolean;
michael@0 1052 CSClient_t *client;
michael@0 1053 PRStatus rv, joinStatus;
michael@0 1054 CSServer_t *server = NULL;
michael@0 1055 char *thread_type;
michael@0 1056
michael@0 1057 PRUintn backlog = DEFAULT_BACKLOG;
michael@0 1058 PRUintn clients = DEFAULT_CLIENTS;
michael@0 1059 const char *serverName = DEFAULT_SERVER;
michael@0 1060 PRBool serverIsLocal = PR_TRUE;
michael@0 1061 PRUintn accepting = ALLOWED_IN_ACCEPT;
michael@0 1062 PRUintn workersMin = DEFAULT_WORKERS_MIN;
michael@0 1063 PRUintn workersMax = DEFAULT_WORKERS_MAX;
michael@0 1064 PRIntn execution = DEFAULT_EXECUTION_TIME;
michael@0 1065
michael@0 1066 /*
michael@0 1067 * -G use global threads
michael@0 1068 * -a <n> threads allowed in accept
michael@0 1069 * -b <n> backlock for listen
michael@0 1070 * -c <threads> number of clients to create
michael@0 1071 * -w <threads> minimal number of server threads
michael@0 1072 * -W <threads> maximum number of server threads
michael@0 1073 * -e <seconds> duration of the test in seconds
michael@0 1074 * -s <string> dsn name of server (implies no server here)
michael@0 1075 * -v verbosity
michael@0 1076 */
michael@0 1077
michael@0 1078 PLOptStatus os;
michael@0 1079 PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:w:W:e:s:T:vdhp");
michael@0 1080
michael@0 1081 #if defined(WIN32)
michael@0 1082 thread_provider = thread_win32;
michael@0 1083 #elif defined(_PR_PTHREADS)
michael@0 1084 thread_provider = thread_pthread;
michael@0 1085 #elif defined(IRIX)
michael@0 1086 thread_provider = thread_sproc;
michael@0 1087 #else
michael@0 1088 thread_provider = thread_nspr;
michael@0 1089 #endif
michael@0 1090
michael@0 1091 debug_out = PR_GetSpecialFD(PR_StandardError);
michael@0 1092
michael@0 1093 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
michael@0 1094 {
michael@0 1095 if (PL_OPT_BAD == os) continue;
michael@0 1096 switch (opt->option)
michael@0 1097 {
michael@0 1098 case 'G': /* use global threads */
michael@0 1099 thread_scope = PR_GLOBAL_THREAD;
michael@0 1100 break;
michael@0 1101 case 'X': /* use XTP as transport */
michael@0 1102 protocol = 36;
michael@0 1103 break;
michael@0 1104 case '6': /* Use IPv6 */
michael@0 1105 domain = PR_AF_INET6;
michael@0 1106 break;
michael@0 1107 case 'a': /* the value for accepting */
michael@0 1108 accepting = atoi(opt->value);
michael@0 1109 break;
michael@0 1110 case 'b': /* the value for backlock */
michael@0 1111 backlog = atoi(opt->value);
michael@0 1112 break;
michael@0 1113 case 'T': /* the thread provider */
michael@0 1114 if ('n' == *opt->value) thread_provider = thread_nspr;
michael@0 1115 else if ('p' == *opt->value) thread_provider = thread_pthread;
michael@0 1116 else if ('w' == *opt->value) thread_provider = thread_win32;
michael@0 1117 else {Help(); return 2; }
michael@0 1118 break;
michael@0 1119 case 'c': /* number of client threads */
michael@0 1120 clients = atoi(opt->value);
michael@0 1121 break;
michael@0 1122 case 'w': /* minimum server worker threads */
michael@0 1123 workersMin = atoi(opt->value);
michael@0 1124 break;
michael@0 1125 case 'W': /* maximum server worker threads */
michael@0 1126 workersMax = atoi(opt->value);
michael@0 1127 break;
michael@0 1128 case 'e': /* program execution time in seconds */
michael@0 1129 execution = atoi(opt->value);
michael@0 1130 break;
michael@0 1131 case 's': /* server's address */
michael@0 1132 serverName = opt->value;
michael@0 1133 break;
michael@0 1134 case 'v': /* verbosity */
michael@0 1135 verbosity = IncrementVerbosity();
michael@0 1136 break;
michael@0 1137 case 'd': /* debug mode */
michael@0 1138 debug_mode = PR_TRUE;
michael@0 1139 break;
michael@0 1140 case 'p': /* pthread mode */
michael@0 1141 pthread_stats = PR_TRUE;
michael@0 1142 break;
michael@0 1143 case 'h':
michael@0 1144 default:
michael@0 1145 Help();
michael@0 1146 return 2;
michael@0 1147 }
michael@0 1148 }
michael@0 1149 PL_DestroyOptState(opt);
michael@0 1150
michael@0 1151 if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) serverIsLocal = PR_FALSE;
michael@0 1152 if (0 == execution) execution = DEFAULT_EXECUTION_TIME;
michael@0 1153 if (0 == workersMax) workersMax = DEFAULT_WORKERS_MAX;
michael@0 1154 if (0 == workersMin) workersMin = DEFAULT_WORKERS_MIN;
michael@0 1155 if (0 == accepting) accepting = ALLOWED_IN_ACCEPT;
michael@0 1156 if (0 == backlog) backlog = DEFAULT_BACKLOG;
michael@0 1157
michael@0 1158 if (workersMin > accepting) accepting = workersMin;
michael@0 1159
michael@0 1160 PR_STDIO_INIT();
michael@0 1161 TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread());
michael@0 1162
michael@0 1163 cltsrv_log_file = PR_NewLogModule("cltsrv_log");
michael@0 1164 MY_ASSERT(NULL != cltsrv_log_file);
michael@0 1165 boolean = PR_SetLogFile("cltsrv.log");
michael@0 1166 MY_ASSERT(boolean);
michael@0 1167
michael@0 1168 if (serverIsLocal)
michael@0 1169 {
michael@0 1170 /* Establish the server */
michael@0 1171 TEST_LOG(
michael@0 1172 cltsrv_log_file, TEST_LOG_INFO,
michael@0 1173 ("main(0x%p): starting server\n", PR_GetCurrentThread()));
michael@0 1174
michael@0 1175 server = PR_NEWZAP(CSServer_t);
michael@0 1176 PR_INIT_CLIST(&server->list);
michael@0 1177 server->state = cs_init;
michael@0 1178 server->ml = PR_NewLock();
michael@0 1179 server->backlog = backlog;
michael@0 1180 server->port = DEFAULT_PORT;
michael@0 1181 server->workers.minimum = workersMin;
michael@0 1182 server->workers.maximum = workersMax;
michael@0 1183 server->workers.accepting = accepting;
michael@0 1184 server->stateChange = PR_NewCondVar(server->ml);
michael@0 1185 server->pool.exiting = PR_NewCondVar(server->ml);
michael@0 1186 server->pool.acceptComplete = PR_NewCondVar(server->ml);
michael@0 1187
michael@0 1188 TEST_LOG(
michael@0 1189 cltsrv_log_file, TEST_LOG_NOTICE,
michael@0 1190 ("main(0x%p): creating server thread\n", PR_GetCurrentThread()));
michael@0 1191
michael@0 1192 rv = NewThread(
michael@0 1193 Server, server, PR_PRIORITY_HIGH, PR_JOINABLE_THREAD);
michael@0 1194 TEST_ASSERT(PR_SUCCESS == rv);
michael@0 1195
michael@0 1196 TEST_LOG(
michael@0 1197 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 1198 ("main(0x%p): waiting for server init\n", PR_GetCurrentThread()));
michael@0 1199
michael@0 1200 PR_Lock(server->ml);
michael@0 1201 while (server->state == cs_init)
michael@0 1202 PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
michael@0 1203 PR_Unlock(server->ml);
michael@0 1204
michael@0 1205 TEST_LOG(
michael@0 1206 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 1207 ("main(0x%p): server init complete (port #%d)\n",
michael@0 1208 PR_GetCurrentThread(), server->port));
michael@0 1209 }
michael@0 1210
michael@0 1211 if (clients != 0)
michael@0 1212 {
michael@0 1213 /* Create all of the clients */
michael@0 1214 PRHostEnt host;
michael@0 1215 char buffer[BUFFER_SIZE];
michael@0 1216 client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t));
michael@0 1217
michael@0 1218 TEST_LOG(
michael@0 1219 cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 1220 ("main(0x%p): creating %d client threads\n",
michael@0 1221 PR_GetCurrentThread(), clients));
michael@0 1222
michael@0 1223 if (!serverIsLocal)
michael@0 1224 {
michael@0 1225 rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host);
michael@0 1226 if (PR_SUCCESS != rv)
michael@0 1227 {
michael@0 1228 PL_FPrintError(PR_STDERR, "PR_GetHostByName");
michael@0 1229 return 2;
michael@0 1230 }
michael@0 1231 }
michael@0 1232
michael@0 1233 for (index = 0; index < clients; ++index)
michael@0 1234 {
michael@0 1235 client[index].state = cs_init;
michael@0 1236 client[index].ml = PR_NewLock();
michael@0 1237 if (serverIsLocal)
michael@0 1238 {
michael@0 1239 (void)PR_InitializeNetAddr(
michael@0 1240 PR_IpAddrLoopback, DEFAULT_PORT,
michael@0 1241 &client[index].serverAddress);
michael@0 1242 }
michael@0 1243 else
michael@0 1244 {
michael@0 1245 (void)PR_EnumerateHostEnt(
michael@0 1246 0, &host, DEFAULT_PORT, &client[index].serverAddress);
michael@0 1247 }
michael@0 1248 client[index].stateChange = PR_NewCondVar(client[index].ml);
michael@0 1249 TEST_LOG(
michael@0 1250 cltsrv_log_file, TEST_LOG_INFO,
michael@0 1251 ("main(0x%p): creating client threads\n", PR_GetCurrentThread()));
michael@0 1252 rv = NewThread(
michael@0 1253 Client, &client[index], PR_PRIORITY_NORMAL, PR_JOINABLE_THREAD);
michael@0 1254 TEST_ASSERT(PR_SUCCESS == rv);
michael@0 1255 PR_Lock(client[index].ml);
michael@0 1256 while (cs_init == client[index].state)
michael@0 1257 PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
michael@0 1258 PR_Unlock(client[index].ml);
michael@0 1259 }
michael@0 1260 }
michael@0 1261
michael@0 1262 /* Then just let them go at it for a bit */
michael@0 1263 TEST_LOG(
michael@0 1264 cltsrv_log_file, TEST_LOG_ALWAYS,
michael@0 1265 ("main(0x%p): waiting for execution interval (%d seconds)\n",
michael@0 1266 PR_GetCurrentThread(), execution));
michael@0 1267
michael@0 1268 WaitForCompletion(execution);
michael@0 1269
michael@0 1270 TimeOfDayMessage("Shutting down", PR_GetCurrentThread());
michael@0 1271
michael@0 1272 if (clients != 0)
michael@0 1273 {
michael@0 1274 for (index = 0; index < clients; ++index)
michael@0 1275 {
michael@0 1276 TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
michael@0 1277 ("main(0x%p): notifying client(0x%p) to stop\n",
michael@0 1278 PR_GetCurrentThread(), client[index].thread));
michael@0 1279
michael@0 1280 PR_Lock(client[index].ml);
michael@0 1281 if (cs_run == client[index].state)
michael@0 1282 {
michael@0 1283 client[index].state = cs_stop;
michael@0 1284 PR_Interrupt(client[index].thread);
michael@0 1285 while (cs_stop == client[index].state)
michael@0 1286 PR_WaitCondVar(
michael@0 1287 client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
michael@0 1288 }
michael@0 1289 PR_Unlock(client[index].ml);
michael@0 1290
michael@0 1291 TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
michael@0 1292 ("main(0x%p): joining client(0x%p)\n",
michael@0 1293 PR_GetCurrentThread(), client[index].thread));
michael@0 1294
michael@0 1295 joinStatus = JoinThread(client[index].thread);
michael@0 1296 TEST_ASSERT(PR_SUCCESS == joinStatus);
michael@0 1297 PR_DestroyCondVar(client[index].stateChange);
michael@0 1298 PR_DestroyLock(client[index].ml);
michael@0 1299 }
michael@0 1300 PR_DELETE(client);
michael@0 1301 }
michael@0 1302
michael@0 1303 if (NULL != server)
michael@0 1304 {
michael@0 1305 /* All clients joined - retrieve the server */
michael@0 1306 TEST_LOG(
michael@0 1307 cltsrv_log_file, TEST_LOG_NOTICE,
michael@0 1308 ("main(0x%p): notifying server(0x%p) to stop\n",
michael@0 1309 PR_GetCurrentThread(), server->thread));
michael@0 1310
michael@0 1311 PR_Lock(server->ml);
michael@0 1312 server->state = cs_stop;
michael@0 1313 PR_Interrupt(server->thread);
michael@0 1314 while (cs_exit != server->state)
michael@0 1315 PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
michael@0 1316 PR_Unlock(server->ml);
michael@0 1317
michael@0 1318 TEST_LOG(
michael@0 1319 cltsrv_log_file, TEST_LOG_NOTICE,
michael@0 1320 ("main(0x%p): joining server(0x%p)\n",
michael@0 1321 PR_GetCurrentThread(), server->thread));
michael@0 1322 joinStatus = JoinThread(server->thread);
michael@0 1323 TEST_ASSERT(PR_SUCCESS == joinStatus);
michael@0 1324
michael@0 1325 PR_DestroyCondVar(server->stateChange);
michael@0 1326 PR_DestroyCondVar(server->pool.exiting);
michael@0 1327 PR_DestroyCondVar(server->pool.acceptComplete);
michael@0 1328 PR_DestroyLock(server->ml);
michael@0 1329 PR_DELETE(server);
michael@0 1330 }
michael@0 1331
michael@0 1332 TEST_LOG(
michael@0 1333 cltsrv_log_file, TEST_LOG_ALWAYS,
michael@0 1334 ("main(0x%p): test complete\n", PR_GetCurrentThread()));
michael@0 1335
michael@0 1336 if (thread_provider == thread_win32)
michael@0 1337 thread_type = "\nWin32 Thread Statistics\n";
michael@0 1338 else if (thread_provider == thread_pthread)
michael@0 1339 thread_type = "\npthread Statistics\n";
michael@0 1340 else if (thread_provider == thread_sproc)
michael@0 1341 thread_type = "\nsproc Statistics\n";
michael@0 1342 else {
michael@0 1343 PR_ASSERT(thread_provider == thread_nspr);
michael@0 1344 thread_type = "\nPRThread Statistics\nn";
michael@0 1345 }
michael@0 1346
michael@0 1347 PT_FPrintStats(debug_out, thread_type);
michael@0 1348
michael@0 1349 TimeOfDayMessage("Test exiting at", PR_GetCurrentThread());
michael@0 1350 PR_Cleanup();
michael@0 1351 return 0;
michael@0 1352 } /* main */
michael@0 1353
michael@0 1354 /* cltsrv.c */

mercurial