servr_uk.c (15051B)
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 return NULL; 233 } 234 } 235 236 sockOpt.option = PR_SockOpt_Reuseaddr; 237 sockOpt.value.reuse_addr = PR_TRUE; 238 if (PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) { 239 if (debug_mode) 240 printf("\tServer error setting socket option: OS error %d\n", 241 PR_GetOSError()); 242 else { 243 failed_already = 1; 244 } 245 PR_Close(listenSocket); 246 return NULL; 247 } 248 249 memset(&serverAddr, 0, sizeof(PRNetAddr)); 250 serverAddr.inet.family = PR_AF_INET; 251 serverAddr.inet.port = PR_htons(PORT); 252 serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY); 253 254 if (PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) { 255 if (debug_mode) 256 printf("\tServer error binding to server address: OS error %d\n", 257 PR_GetOSError()); 258 else { 259 failed_already = 1; 260 } 261 PR_Close(listenSocket); 262 return NULL; 263 } 264 265 if (PR_Listen(listenSocket, 128) == PR_FAILURE) { 266 if (debug_mode) { 267 printf("\tServer error listening to server socket\n"); 268 } else { 269 failed_already = 1; 270 } 271 PR_Close(listenSocket); 272 273 return NULL; 274 } 275 276 /* Create Clients */ 277 workerThreads = 0; 278 workerThreadsBusy = 0; 279 280 workerThreadsLock = PR_NewLock(); 281 282 WorkerThread = PR_CreateThread(PR_SYSTEM_THREAD, WorkerThreadFunc, 283 listenSocket, PR_PRIORITY_NORMAL, ServerScope, 284 PR_UNJOINABLE_THREAD, THREAD_STACKSIZE); 285 286 if (!WorkerThread) { 287 if (debug_mode) { 288 printf("error creating working thread\n"); 289 } 290 PR_Close(listenSocket); 291 return NULL; 292 } 293 PR_AtomicIncrement(&workerThreads); 294 if (debug_mode) { 295 DPRINTF("\tServer created primordial worker thread\n"); 296 } 297 298 return listenSocket; 299 } 300 301 /* The main server loop */ 302 void ServerThreadFunc(void* unused) { 303 PRFileDesc* listenSocket; 304 305 /* Do setup */ 306 listenSocket = ServerSetup(); 307 308 if (!listenSocket) { 309 SetServerState(SERVER, SERVER_STATE_DEAD); 310 } else { 311 if (debug_mode) { 312 DPRINTF("\tServer up\n"); 313 } 314 315 /* Tell clients they can start now. */ 316 SetServerState(SERVER, SERVER_STATE_READY); 317 318 /* Now wait for server death signal */ 319 WaitServerState(SERVER, SERVER_STATE_DYING); 320 321 /* Cleanup */ 322 SetServerState(SERVER, SERVER_STATE_DEAD); 323 } 324 } 325 326 /* --- Client Functions ------------------------------------------- */ 327 328 PRInt32 numRequests; 329 PRInt32 numClients; 330 PRMonitor* clientMonitor; 331 332 void ClientThreadFunc(void* unused) { 333 PRNetAddr serverAddr; 334 PRFileDesc* clientSocket; 335 char* sendBuf; 336 char* recvBuf; 337 PRInt32 rv; 338 PRInt32 bytesNeeded; 339 340 sendBuf = (char*)PR_MALLOC(_client_data * sizeof(char)); 341 if (!sendBuf) 342 if (debug_mode) { 343 printf("\tClient could not malloc space!?\n"); 344 } 345 recvBuf = (char*)PR_MALLOC(_server_data * sizeof(char)); 346 if (!recvBuf) 347 if (debug_mode) { 348 printf("\tClient could not malloc space!?\n"); 349 } 350 351 memset(&serverAddr, 0, sizeof(PRNetAddr)); 352 serverAddr.inet.family = PR_AF_INET; 353 serverAddr.inet.port = PR_htons(PORT); 354 serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); 355 356 while (numRequests > 0) { 357 if ((numRequests % 10) == 0) 358 if (debug_mode) { 359 printf("."); 360 } 361 if (debug_mode) { 362 DPRINTF("\tClient starting request %d\n", numRequests); 363 } 364 365 clientSocket = PR_NewTCPSocket(); 366 if (!clientSocket) { 367 if (debug_mode) 368 printf("Client error creating socket: OS error %d\n", PR_GetOSError()); 369 continue; 370 } 371 372 if (debug_mode) { 373 DPRINTF("\tClient connecting\n"); 374 } 375 376 rv = PR_Connect(clientSocket, &serverAddr, PR_INTERVAL_NO_TIMEOUT); 377 if (!clientSocket) { 378 if (debug_mode) { 379 printf("\tClient error connecting\n"); 380 } 381 continue; 382 } 383 384 if (debug_mode) { 385 DPRINTF("\tClient connected\n"); 386 } 387 388 rv = 389 PR_Send(clientSocket, sendBuf, _client_data, 0, PR_INTERVAL_NO_TIMEOUT); 390 if (rv != _client_data) { 391 if (debug_mode) { 392 printf("Client error sending data (%d)\n", rv); 393 } 394 PR_Close(clientSocket); 395 continue; 396 } 397 398 if (debug_mode) { 399 DPRINTF("\tClient sent %d bytes\n", rv); 400 } 401 402 bytesNeeded = _server_data; 403 while (bytesNeeded) { 404 rv = PR_Recv(clientSocket, recvBuf, bytesNeeded, 0, 405 PR_INTERVAL_NO_TIMEOUT); 406 if (rv <= 0) { 407 if (debug_mode) 408 printf("Client error receiving data (%d) (%d/%d)\n", rv, 409 (_server_data - bytesNeeded), _server_data); 410 break; 411 } 412 if (debug_mode) { 413 DPRINTF("\tClient received %d bytes; need %d more\n", rv, 414 bytesNeeded - rv); 415 } 416 bytesNeeded -= rv; 417 } 418 419 PR_Close(clientSocket); 420 421 PR_AtomicDecrement(&numRequests); 422 } 423 424 PR_EnterMonitor(clientMonitor); 425 --numClients; 426 PR_Notify(clientMonitor); 427 PR_ExitMonitor(clientMonitor); 428 429 PR_DELETE(sendBuf); 430 PR_DELETE(recvBuf); 431 } 432 433 void RunClients(void) { 434 PRInt32 index; 435 436 numRequests = _iterations; 437 numClients = _clients; 438 clientMonitor = PR_NewMonitor(); 439 440 for (index = 0; index < _clients; index++) { 441 PRThread* clientThread; 442 443 clientThread = PR_CreateThread(PR_USER_THREAD, ClientThreadFunc, NULL, 444 PR_PRIORITY_NORMAL, ClientScope, 445 PR_UNJOINABLE_THREAD, THREAD_STACKSIZE); 446 447 if (!clientThread) { 448 if (debug_mode) { 449 printf("\terror creating client thread %d\n", index); 450 } 451 } else if (debug_mode) { 452 DPRINTF("\tMain created client %d/%d\n", index + 1, _clients); 453 } 454 } 455 456 PR_EnterMonitor(clientMonitor); 457 while (numClients) { 458 PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT); 459 } 460 PR_ExitMonitor(clientMonitor); 461 } 462 463 /* --- Main Function ---------------------------------------------- */ 464 465 static void do_work() { 466 PRThread* ServerThread; 467 PRInt32 state; 468 469 SetServerState(MAIN, SERVER_STATE_STARTUP); 470 ServerThread = PR_CreateThread(PR_USER_THREAD, ServerThreadFunc, NULL, 471 PR_PRIORITY_NORMAL, ServerScope, 472 PR_JOINABLE_THREAD, THREAD_STACKSIZE); 473 if (!ServerThread) { 474 if (debug_mode) { 475 printf("error creating main server thread\n"); 476 } 477 return; 478 } 479 480 /* Wait for server to be ready */ 481 state = WaitServerState(MAIN, SERVER_STATE_READY | SERVER_STATE_DEAD); 482 483 if (!(state & SERVER_STATE_DEAD)) { 484 /* Run Test Clients */ 485 RunClients(); 486 487 /* Send death signal to server */ 488 SetServerState(MAIN, SERVER_STATE_DYING); 489 } 490 491 PR_JoinThread(ServerThread); 492 } 493 494 static void do_workUK(void) { 495 ServerScope = PR_LOCAL_THREAD; 496 ClientScope = PR_GLOBAL_THREAD; 497 do_work(); 498 } 499 500 static void Measure(void (*func)(void), const char* msg) { 501 PRIntervalTime start, stop; 502 double d; 503 504 start = PR_IntervalNow(); 505 (*func)(); 506 stop = PR_IntervalNow(); 507 508 d = (double)PR_IntervalToMicroseconds(stop - start); 509 510 if (debug_mode) { 511 printf("\n%40s: %6.2f usec\n", msg, d / _iterations); 512 } 513 } 514 515 int main(int argc, char** argv) { 516 /* The command line argument: -d is used to determine if the test is being run 517 in debug mode. The regress tool requires only one line output:PASS or FAIL. 518 All of the printfs associated with this test has been handled with a if 519 (debug_mode) test. Usage: test_name -d 520 */ 521 PLOptStatus os; 522 PLOptState* opt = PL_CreateOptState(argc, argv, "d:"); 523 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { 524 if (PL_OPT_BAD == os) { 525 continue; 526 } 527 switch (opt->option) { 528 case 'd': /* debug mode */ 529 debug_mode = 1; 530 break; 531 default: 532 break; 533 } 534 } 535 PL_DestroyOptState(opt); 536 537 /* main test */ 538 if (debug_mode) { 539 printf("Enter number of iterations: \n"); 540 scanf("%d", &_iterations); 541 printf("Enter number of clients : \n"); 542 scanf("%d", &_clients); 543 printf("Enter size of client data : \n"); 544 scanf("%d", &_client_data); 545 printf("Enter size of server data : \n"); 546 scanf("%d", &_server_data); 547 } else { 548 _iterations = 7; 549 _clients = 7; 550 _client_data = 100; 551 _server_data = 100; 552 } 553 554 if (debug_mode) { 555 printf("\n\n%d iterations with %d client threads.\n", _iterations, 556 _clients); 557 printf("Sending %d bytes of client data and %d bytes of server data\n", 558 _client_data, _server_data); 559 } 560 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 561 562 PR_SetThreadRecycleMode(64); 563 564 ServerStateCVLock = PR_NewLock(); 565 ServerStateCV = PR_NewCondVar(ServerStateCVLock); 566 567 Measure(do_workUK, "server loop user/kernel"); 568 569 PR_Cleanup(); 570 571 if (failed_already) { 572 return 1; 573 } else { 574 return 0; 575 } 576 }