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