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