server_test.c (16137B)
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 ** This server simulates a server running in loopback mode. 9 ** 10 ** The idea is that a single server is created. The server initially creates 11 ** a number of worker threads. Then, with the server running, a number of 12 ** clients are created which start requesting service from the server. 13 ** 14 ** 15 ** Modification History: 16 ** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. 17 ** The debug mode will print all of the printfs associated with this 18 *test. 19 ** The regress mode will be the default mode. Since the regress tool 20 *limits 21 ** the output to a one line status:PASS or FAIL,all of the printf 22 *statements 23 ** have been handled with an if (debug_mode) statement. 24 ***********************************************************************/ 25 26 /*********************************************************************** 27 ** Includes 28 ***********************************************************************/ 29 /* Used to get the command line option */ 30 #include "plgetopt.h" 31 32 #include "nspr.h" 33 #include "pprthred.h" 34 35 #include <string.h> 36 37 #define PORT 15004 38 #define THREAD_STACKSIZE 0 39 40 #define PASS 0 41 #define FAIL 1 42 static int debug_mode = 0; 43 static int failed_already = 0; 44 45 static int _iterations = 1000; 46 static int _clients = 1; 47 static int _client_data = 250; 48 static int _server_data = (8 * 1024); 49 50 static PRThreadScope ServerScope, ClientScope; 51 52 #define SERVER "Server" 53 #define MAIN "Main" 54 55 #define SERVER_STATE_STARTUP 0 56 #define SERVER_STATE_READY 1 57 #define SERVER_STATE_DYING 2 58 #define SERVER_STATE_DEAD 4 59 int ServerState; 60 PRLock* ServerStateCVLock; 61 PRCondVar* ServerStateCV; 62 63 #undef DEBUGPRINTS 64 #ifdef DEBUGPRINTS 65 # define DPRINTF printf 66 #else 67 # define DPRINTF 68 #endif 69 70 /*********************************************************************** 71 ** PRIVATE FUNCTION: Test_Result 72 ** DESCRIPTION: Used in conjunction with the regress tool, prints out the 73 ** status of the test case. 74 ** INPUTS: PASS/FAIL 75 ** OUTPUTS: None 76 ** RETURN: None 77 ** SIDE EFFECTS: 78 ** 79 ** RESTRICTIONS: 80 ** None 81 ** MEMORY: NA 82 ** ALGORITHM: Determine what the status is and print accordingly. 83 ** 84 ***********************************************************************/ 85 86 static void Test_Result(int result) { 87 switch (result) { 88 case PASS: 89 printf("PASS\n"); 90 break; 91 case FAIL: 92 printf("FAIL\n"); 93 failed_already = 1; 94 break; 95 default: 96 break; 97 } 98 } 99 100 static void do_work(void); 101 102 /* --- Server state functions --------------------------------------------- */ 103 void SetServerState(char* waiter, PRInt32 state) { 104 PR_Lock(ServerStateCVLock); 105 ServerState = state; 106 PR_NotifyCondVar(ServerStateCV); 107 108 if (debug_mode) { 109 DPRINTF("\t%s changed state to %d\n", waiter, state); 110 } 111 112 PR_Unlock(ServerStateCVLock); 113 } 114 115 int WaitServerState(char* waiter, PRInt32 state) { 116 PRInt32 rv; 117 118 PR_Lock(ServerStateCVLock); 119 120 if (debug_mode) { 121 DPRINTF("\t%s waiting for state %d\n", waiter, state); 122 } 123 124 while (!(ServerState & state)) { 125 PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT); 126 } 127 rv = ServerState; 128 129 if (debug_mode) 130 DPRINTF("\t%s resuming from wait for state %d; state now %d\n", waiter, 131 state, ServerState); 132 PR_Unlock(ServerStateCVLock); 133 134 return rv; 135 } 136 137 /* --- Server Functions ------------------------------------------- */ 138 139 PRLock* workerThreadsLock; 140 PRInt32 workerThreads; 141 PRInt32 workerThreadsBusy; 142 143 void WorkerThreadFunc(void* _listenSock) { 144 PRFileDesc* listenSock = (PRFileDesc*)_listenSock; 145 PRInt32 bytesRead; 146 PRInt32 bytesWritten; 147 char* dataBuf; 148 char* sendBuf; 149 150 if (debug_mode) 151 DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n", 152 _client_data + (2 * sizeof(PRNetAddr)) + 32, _client_data, 153 (2 * sizeof(PRNetAddr)) + 32); 154 dataBuf = (char*)PR_MALLOC(_client_data + 2 * sizeof(PRNetAddr) + 32); 155 if (!dataBuf) 156 if (debug_mode) { 157 printf("\tServer could not malloc space!?\n"); 158 } 159 sendBuf = (char*)PR_MALLOC(_server_data * sizeof(char)); 160 if (!sendBuf) 161 if (debug_mode) { 162 printf("\tServer could not malloc space!?\n"); 163 } 164 165 if (debug_mode) { 166 DPRINTF("\tServer worker thread running\n"); 167 } 168 169 while (1) { 170 PRInt32 bytesToRead = _client_data; 171 PRInt32 bytesToWrite = _server_data; 172 PRFileDesc* newSock; 173 PRNetAddr* rAddr; 174 PRInt32 loops = 0; 175 176 loops++; 177 178 if (debug_mode) { 179 DPRINTF("\tServer thread going into accept\n"); 180 } 181 182 bytesRead = PR_AcceptRead(listenSock, &newSock, &rAddr, dataBuf, 183 bytesToRead, PR_INTERVAL_NO_TIMEOUT); 184 185 if (bytesRead < 0) { 186 if (debug_mode) { 187 printf("\tServer error in accept (%d)\n", bytesRead); 188 } 189 continue; 190 } 191 192 if (debug_mode) { 193 DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead); 194 } 195 196 PR_AtomicIncrement(&workerThreadsBusy); 197 if (workerThreadsBusy == workerThreads) { 198 PR_Lock(workerThreadsLock); 199 if (workerThreadsBusy == workerThreads) { 200 PRThread* WorkerThread; 201 202 WorkerThread = PR_CreateThread( 203 PR_SYSTEM_THREAD, WorkerThreadFunc, listenSock, PR_PRIORITY_NORMAL, 204 ServerScope, PR_UNJOINABLE_THREAD, THREAD_STACKSIZE); 205 206 if (!WorkerThread) { 207 if (debug_mode) { 208 printf("Error creating client thread %d\n", workerThreads); 209 } 210 } else { 211 PR_AtomicIncrement(&workerThreads); 212 if (debug_mode) { 213 DPRINTF("\tServer creates worker (%d)\n", workerThreads); 214 } 215 } 216 } 217 PR_Unlock(workerThreadsLock); 218 } 219 220 bytesToRead -= bytesRead; 221 while (bytesToRead) { 222 bytesRead = 223 PR_Recv(newSock, dataBuf, bytesToRead, 0, PR_INTERVAL_NO_TIMEOUT); 224 if (bytesRead < 0) { 225 if (debug_mode) { 226 printf("\tServer error receiving data (%d)\n", bytesRead); 227 } 228 continue; 229 } 230 if (debug_mode) { 231 DPRINTF("\tServer received %d bytes\n", bytesRead); 232 } 233 } 234 235 bytesWritten = 236 PR_Send(newSock, sendBuf, bytesToWrite, 0, PR_INTERVAL_NO_TIMEOUT); 237 if (bytesWritten != _server_data) { 238 if (debug_mode) 239 printf("\tError sending data to client (%d, %d)\n", bytesWritten, 240 PR_GetOSError()); 241 } else { 242 if (debug_mode) { 243 DPRINTF("\tServer sent %d bytes\n", bytesWritten); 244 } 245 } 246 247 PR_Close(newSock); 248 PR_AtomicDecrement(&workerThreadsBusy); 249 } 250 } 251 252 PRFileDesc* ServerSetup(void) { 253 PRFileDesc* listenSocket; 254 PRSocketOptionData sockOpt; 255 PRNetAddr serverAddr; 256 PRThread* WorkerThread; 257 258 if ((listenSocket = PR_NewTCPSocket()) == NULL) { 259 if (debug_mode) { 260 printf("\tServer error creating listen socket\n"); 261 } else { 262 Test_Result(FAIL); 263 } 264 return NULL; 265 } 266 267 sockOpt.option = PR_SockOpt_Reuseaddr; 268 sockOpt.value.reuse_addr = PR_TRUE; 269 if (PR_SetSocketOption(listenSocket, &sockOpt) != PR_SUCCESS) { 270 if (debug_mode) 271 printf("\tServer error setting socket option: OS error %d\n", 272 PR_GetOSError()); 273 else { 274 Test_Result(FAIL); 275 } 276 PR_Close(listenSocket); 277 return NULL; 278 } 279 280 memset(&serverAddr, 0, sizeof(PRNetAddr)); 281 serverAddr.inet.family = PR_AF_INET; 282 serverAddr.inet.port = PR_htons(PORT); 283 serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY); 284 285 if (PR_Bind(listenSocket, &serverAddr) != PR_SUCCESS) { 286 if (debug_mode) 287 printf("\tServer error binding to server address: OS error %d\n", 288 PR_GetOSError()); 289 else { 290 Test_Result(FAIL); 291 } 292 PR_Close(listenSocket); 293 return NULL; 294 } 295 296 if (PR_Listen(listenSocket, 128) != PR_SUCCESS) { 297 if (debug_mode) { 298 printf("\tServer error listening to server socket\n"); 299 } else { 300 Test_Result(FAIL); 301 } 302 PR_Close(listenSocket); 303 304 return NULL; 305 } 306 307 /* Create Clients */ 308 workerThreads = 0; 309 workerThreadsBusy = 0; 310 311 workerThreadsLock = PR_NewLock(); 312 313 WorkerThread = PR_CreateThread(PR_SYSTEM_THREAD, WorkerThreadFunc, 314 listenSocket, PR_PRIORITY_NORMAL, ServerScope, 315 PR_UNJOINABLE_THREAD, THREAD_STACKSIZE); 316 317 if (!WorkerThread) { 318 if (debug_mode) { 319 printf("error creating working thread\n"); 320 } 321 PR_Close(listenSocket); 322 return NULL; 323 } 324 PR_AtomicIncrement(&workerThreads); 325 if (debug_mode) { 326 DPRINTF("\tServer created primordial worker thread\n"); 327 } 328 329 return listenSocket; 330 } 331 332 /* The main server loop */ 333 void ServerThreadFunc(void* unused) { 334 PRFileDesc* listenSocket; 335 336 /* Do setup */ 337 listenSocket = ServerSetup(); 338 339 if (!listenSocket) { 340 SetServerState(SERVER, SERVER_STATE_DEAD); 341 } else { 342 if (debug_mode) { 343 DPRINTF("\tServer up\n"); 344 } 345 346 /* Tell clients they can start now. */ 347 SetServerState(SERVER, SERVER_STATE_READY); 348 349 /* Now wait for server death signal */ 350 WaitServerState(SERVER, SERVER_STATE_DYING); 351 352 /* Cleanup */ 353 SetServerState(SERVER, SERVER_STATE_DEAD); 354 } 355 } 356 357 /* --- Client Functions ------------------------------------------- */ 358 359 PRInt32 numRequests; 360 PRInt32 numClients; 361 PRMonitor* clientMonitor; 362 363 void ClientThreadFunc(void* unused) { 364 PRNetAddr serverAddr; 365 PRFileDesc* clientSocket; 366 char* sendBuf; 367 char* recvBuf; 368 PRInt32 rv; 369 PRInt32 bytesNeeded; 370 371 sendBuf = (char*)PR_MALLOC(_client_data * sizeof(char)); 372 if (!sendBuf) 373 if (debug_mode) { 374 printf("\tClient could not malloc space!?\n"); 375 } 376 recvBuf = (char*)PR_MALLOC(_server_data * sizeof(char)); 377 if (!recvBuf) 378 if (debug_mode) { 379 printf("\tClient could not malloc space!?\n"); 380 } 381 382 memset(&serverAddr, 0, sizeof(PRNetAddr)); 383 serverAddr.inet.family = PR_AF_INET; 384 serverAddr.inet.port = PR_htons(PORT); 385 serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); 386 387 while (numRequests > 0) { 388 if ((numRequests % 10) == 0) 389 if (debug_mode) { 390 printf("."); 391 } 392 if (debug_mode) { 393 DPRINTF("\tClient starting request %d\n", numRequests); 394 } 395 396 clientSocket = PR_NewTCPSocket(); 397 if (!clientSocket) { 398 if (debug_mode) 399 printf("Client error creating socket: OS error %d\n", PR_GetOSError()); 400 continue; 401 } 402 403 if (debug_mode) { 404 DPRINTF("\tClient connecting\n"); 405 } 406 407 rv = PR_Connect(clientSocket, &serverAddr, PR_INTERVAL_NO_TIMEOUT); 408 if (!clientSocket) { 409 if (debug_mode) { 410 printf("\tClient error connecting\n"); 411 } 412 continue; 413 } 414 415 if (debug_mode) { 416 DPRINTF("\tClient connected\n"); 417 } 418 419 rv = 420 PR_Send(clientSocket, sendBuf, _client_data, 0, PR_INTERVAL_NO_TIMEOUT); 421 if (rv != _client_data) { 422 if (debug_mode) { 423 printf("Client error sending data (%d)\n", rv); 424 } 425 PR_Close(clientSocket); 426 continue; 427 } 428 429 if (debug_mode) { 430 DPRINTF("\tClient sent %d bytes\n", rv); 431 } 432 433 bytesNeeded = _server_data; 434 while (bytesNeeded) { 435 rv = PR_Recv(clientSocket, recvBuf, bytesNeeded, 0, 436 PR_INTERVAL_NO_TIMEOUT); 437 if (rv <= 0) { 438 if (debug_mode) 439 printf("Client error receiving data (%d) (%d/%d)\n", rv, 440 (_server_data - bytesNeeded), _server_data); 441 break; 442 } 443 if (debug_mode) { 444 DPRINTF("\tClient received %d bytes; need %d more\n", rv, 445 bytesNeeded - rv); 446 } 447 bytesNeeded -= rv; 448 } 449 450 PR_Close(clientSocket); 451 452 PR_AtomicDecrement(&numRequests); 453 } 454 455 PR_EnterMonitor(clientMonitor); 456 --numClients; 457 PR_Notify(clientMonitor); 458 PR_ExitMonitor(clientMonitor); 459 460 PR_DELETE(sendBuf); 461 PR_DELETE(recvBuf); 462 } 463 464 void RunClients(void) { 465 PRInt32 index; 466 467 numRequests = _iterations; 468 numClients = _clients; 469 clientMonitor = PR_NewMonitor(); 470 471 for (index = 0; index < _clients; index++) { 472 PRThread* clientThread; 473 474 clientThread = PR_CreateThread(PR_USER_THREAD, ClientThreadFunc, NULL, 475 PR_PRIORITY_NORMAL, ClientScope, 476 PR_UNJOINABLE_THREAD, THREAD_STACKSIZE); 477 478 if (!clientThread) { 479 if (debug_mode) { 480 printf("\terror creating client thread %d\n", index); 481 } 482 } else if (debug_mode) { 483 DPRINTF("\tMain created client %d/%d\n", index + 1, _clients); 484 } 485 } 486 487 PR_EnterMonitor(clientMonitor); 488 while (numClients) { 489 PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT); 490 } 491 PR_ExitMonitor(clientMonitor); 492 } 493 494 /* --- Main Function ---------------------------------------------- */ 495 496 static void do_work() { 497 PRThread* ServerThread; 498 PRInt32 state; 499 500 SetServerState(MAIN, SERVER_STATE_STARTUP); 501 ServerThread = PR_CreateThread(PR_USER_THREAD, ServerThreadFunc, NULL, 502 PR_PRIORITY_NORMAL, ServerScope, 503 PR_JOINABLE_THREAD, THREAD_STACKSIZE); 504 if (!ServerThread) { 505 if (debug_mode) { 506 printf("error creating main server thread\n"); 507 } 508 return; 509 } 510 511 /* Wait for server to be ready */ 512 state = WaitServerState(MAIN, SERVER_STATE_READY | SERVER_STATE_DEAD); 513 514 if (!(state & SERVER_STATE_DEAD)) { 515 /* Run Test Clients */ 516 RunClients(); 517 518 /* Send death signal to server */ 519 SetServerState(MAIN, SERVER_STATE_DYING); 520 } 521 522 PR_JoinThread(ServerThread); 523 } 524 525 static void do_workUU(void) { 526 ServerScope = PR_LOCAL_THREAD; 527 ClientScope = PR_LOCAL_THREAD; 528 do_work(); 529 } 530 531 static void do_workUK(void) { 532 ServerScope = PR_LOCAL_THREAD; 533 ClientScope = PR_GLOBAL_THREAD; 534 do_work(); 535 } 536 537 static void do_workKU(void) { 538 ServerScope = PR_GLOBAL_THREAD; 539 ClientScope = PR_LOCAL_THREAD; 540 do_work(); 541 } 542 543 static void do_workKK(void) { 544 ServerScope = PR_GLOBAL_THREAD; 545 ClientScope = PR_GLOBAL_THREAD; 546 do_work(); 547 } 548 549 static void Measure(void (*func)(void), const char* msg) { 550 PRIntervalTime start, stop; 551 double d; 552 553 start = PR_IntervalNow(); 554 (*func)(); 555 stop = PR_IntervalNow(); 556 557 d = (double)PR_IntervalToMicroseconds(stop - start); 558 559 if (debug_mode) { 560 printf("\n%40s: %6.2f usec\n", msg, d / _iterations); 561 } 562 } 563 564 int main(int argc, char** argv) { 565 /* The command line argument: -d is used to determine if the test is being run 566 in debug mode. The regress tool requires only one line output:PASS or FAIL. 567 All of the printfs associated with this test has been handled with a if 568 (debug_mode) test. Usage: test_name -d 569 */ 570 PLOptStatus os; 571 PLOptState* opt = PL_CreateOptState(argc, argv, "d"); 572 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { 573 if (PL_OPT_BAD == os) { 574 continue; 575 } 576 switch (opt->option) { 577 case 'd': /* debug mode */ 578 debug_mode = 1; 579 break; 580 default: 581 break; 582 } 583 } 584 PL_DestroyOptState(opt); 585 586 /* main test */ 587 if (debug_mode) { 588 printf("Enter number of iterations: \n"); 589 scanf("%d", &_iterations); 590 printf("Enter number of clients : \n"); 591 scanf("%d", &_clients); 592 printf("Enter size of client data : \n"); 593 scanf("%d", &_client_data); 594 printf("Enter size of server data : \n"); 595 scanf("%d", &_server_data); 596 } else { 597 _iterations = 10; 598 _clients = 1; 599 _client_data = 10; 600 _server_data = 10; 601 } 602 603 if (debug_mode) { 604 printf("\n\n%d iterations with %d client threads.\n", _iterations, 605 _clients); 606 printf("Sending %d bytes of client data and %d bytes of server data\n", 607 _client_data, _server_data); 608 } 609 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 610 611 ServerStateCVLock = PR_NewLock(); 612 ServerStateCV = PR_NewCondVar(ServerStateCVLock); 613 614 Measure(do_workUU, "server loop user/user"); 615 #if 0 616 Measure(do_workUK, "server loop user/kernel"); 617 Measure(do_workKU, "server loop kernel/user"); 618 Measure(do_workKK, "server loop kernel/kernel"); 619 #endif 620 621 PR_Cleanup(); 622 623 return failed_already; 624 }