1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/tests/server_test.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,610 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/*********************************************************************** 1.10 +** 1.11 +** This server simulates a server running in loopback mode. 1.12 +** 1.13 +** The idea is that a single server is created. The server initially creates 1.14 +** a number of worker threads. Then, with the server running, a number of 1.15 +** clients are created which start requesting service from the server. 1.16 +** 1.17 +** 1.18 +** Modification History: 1.19 +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. 1.20 +** The debug mode will print all of the printfs associated with this test. 1.21 +** The regress mode will be the default mode. Since the regress tool limits 1.22 +** the output to a one line status:PASS or FAIL,all of the printf statements 1.23 +** have been handled with an if (debug_mode) statement. 1.24 +***********************************************************************/ 1.25 + 1.26 +/*********************************************************************** 1.27 +** Includes 1.28 +***********************************************************************/ 1.29 +/* Used to get the command line option */ 1.30 +#include "plgetopt.h" 1.31 + 1.32 +#include "nspr.h" 1.33 +#include "pprthred.h" 1.34 + 1.35 +#include <string.h> 1.36 + 1.37 +#define PORT 15004 1.38 +#define THREAD_STACKSIZE 0 1.39 + 1.40 +#define PASS 0 1.41 +#define FAIL 1 1.42 +static int debug_mode = 0; 1.43 + 1.44 +static int _iterations = 1000; 1.45 +static int _clients = 1; 1.46 +static int _client_data = 250; 1.47 +static int _server_data = (8*1024); 1.48 + 1.49 +static PRThreadScope ServerScope, ClientScope; 1.50 + 1.51 +#define SERVER "Server" 1.52 +#define MAIN "Main" 1.53 + 1.54 +#define SERVER_STATE_STARTUP 0 1.55 +#define SERVER_STATE_READY 1 1.56 +#define SERVER_STATE_DYING 2 1.57 +#define SERVER_STATE_DEAD 4 1.58 +int ServerState; 1.59 +PRLock *ServerStateCVLock; 1.60 +PRCondVar *ServerStateCV; 1.61 + 1.62 +#undef DEBUGPRINTS 1.63 +#ifdef DEBUGPRINTS 1.64 +#define DPRINTF printf 1.65 +#else 1.66 +#define DPRINTF 1.67 +#endif 1.68 + 1.69 + 1.70 +/*********************************************************************** 1.71 +** PRIVATE FUNCTION: Test_Result 1.72 +** DESCRIPTION: Used in conjunction with the regress tool, prints out the 1.73 +** status of the test case. 1.74 +** INPUTS: PASS/FAIL 1.75 +** OUTPUTS: None 1.76 +** RETURN: None 1.77 +** SIDE EFFECTS: 1.78 +** 1.79 +** RESTRICTIONS: 1.80 +** None 1.81 +** MEMORY: NA 1.82 +** ALGORITHM: Determine what the status is and print accordingly. 1.83 +** 1.84 +***********************************************************************/ 1.85 + 1.86 + 1.87 +static void Test_Result (int result) 1.88 +{ 1.89 + switch (result) 1.90 + { 1.91 + case PASS: 1.92 + printf ("PASS\n"); 1.93 + break; 1.94 + case FAIL: 1.95 + printf ("FAIL\n"); 1.96 + break; 1.97 + default: 1.98 + break; 1.99 + } 1.100 +} 1.101 + 1.102 +static void do_work(void); 1.103 + 1.104 +/* --- Server state functions --------------------------------------------- */ 1.105 +void 1.106 +SetServerState(char *waiter, PRInt32 state) 1.107 +{ 1.108 + PR_Lock(ServerStateCVLock); 1.109 + ServerState = state; 1.110 + PR_NotifyCondVar(ServerStateCV); 1.111 + 1.112 + if (debug_mode) DPRINTF("\t%s changed state to %d\n", waiter, state); 1.113 + 1.114 + PR_Unlock(ServerStateCVLock); 1.115 +} 1.116 + 1.117 +int 1.118 +WaitServerState(char *waiter, PRInt32 state) 1.119 +{ 1.120 + PRInt32 rv; 1.121 + 1.122 + PR_Lock(ServerStateCVLock); 1.123 + 1.124 + if (debug_mode) DPRINTF("\t%s waiting for state %d\n", waiter, state); 1.125 + 1.126 + while(!(ServerState & state)) 1.127 + PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT); 1.128 + rv = ServerState; 1.129 + 1.130 + if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n", 1.131 + waiter, state, ServerState); 1.132 + PR_Unlock(ServerStateCVLock); 1.133 + 1.134 + return rv; 1.135 +} 1.136 + 1.137 +/* --- Server Functions ------------------------------------------- */ 1.138 + 1.139 +PRLock *workerThreadsLock; 1.140 +PRInt32 workerThreads; 1.141 +PRInt32 workerThreadsBusy; 1.142 + 1.143 +void 1.144 +WorkerThreadFunc(void *_listenSock) 1.145 +{ 1.146 + PRFileDesc *listenSock = (PRFileDesc *)_listenSock; 1.147 + PRInt32 bytesRead; 1.148 + PRInt32 bytesWritten; 1.149 + char *dataBuf; 1.150 + char *sendBuf; 1.151 + 1.152 + if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n", 1.153 + _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32); 1.154 + dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32); 1.155 + if (!dataBuf) 1.156 + if (debug_mode) printf("\tServer could not malloc space!?\n"); 1.157 + sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char)); 1.158 + if (!sendBuf) 1.159 + if (debug_mode) printf("\tServer could not malloc space!?\n"); 1.160 + 1.161 + if (debug_mode) DPRINTF("\tServer worker thread running\n"); 1.162 + 1.163 + while(1) { 1.164 + PRInt32 bytesToRead = _client_data; 1.165 + PRInt32 bytesToWrite = _server_data; 1.166 + PRFileDesc *newSock; 1.167 + PRNetAddr *rAddr; 1.168 + PRInt32 loops = 0; 1.169 + 1.170 + loops++; 1.171 + 1.172 + if (debug_mode) DPRINTF("\tServer thread going into accept\n"); 1.173 + 1.174 + bytesRead = PR_AcceptRead(listenSock, 1.175 + &newSock, 1.176 + &rAddr, 1.177 + dataBuf, 1.178 + bytesToRead, 1.179 + PR_INTERVAL_NO_TIMEOUT); 1.180 + 1.181 + if (bytesRead < 0) { 1.182 + if (debug_mode) printf("\tServer error in accept (%d)\n", bytesRead); 1.183 + continue; 1.184 + } 1.185 + 1.186 + if (debug_mode) DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead); 1.187 + 1.188 + PR_AtomicIncrement(&workerThreadsBusy); 1.189 +#ifdef SYMBIAN 1.190 + if (workerThreadsBusy == workerThreads && workerThreads<1) { 1.191 +#else 1.192 + if (workerThreadsBusy == workerThreads) { 1.193 +#endif 1.194 + PR_Lock(workerThreadsLock); 1.195 + if (workerThreadsBusy == workerThreads) { 1.196 + PRThread *WorkerThread; 1.197 + 1.198 + WorkerThread = PR_CreateThread( 1.199 + PR_SYSTEM_THREAD, 1.200 + WorkerThreadFunc, 1.201 + listenSock, 1.202 + PR_PRIORITY_NORMAL, 1.203 + ServerScope, 1.204 + PR_UNJOINABLE_THREAD, 1.205 + THREAD_STACKSIZE); 1.206 + 1.207 + if (!WorkerThread) { 1.208 + if (debug_mode) printf("Error creating client thread %d\n", workerThreads); 1.209 + } else { 1.210 + PR_AtomicIncrement(&workerThreads); 1.211 + if (debug_mode) DPRINTF("\tServer creates worker (%d)\n", workerThreads); 1.212 + } 1.213 + } 1.214 + PR_Unlock(workerThreadsLock); 1.215 + } 1.216 + 1.217 + bytesToRead -= bytesRead; 1.218 + while (bytesToRead) { 1.219 + bytesRead = PR_Recv(newSock, 1.220 + dataBuf, 1.221 + bytesToRead, 1.222 + 0, 1.223 + PR_INTERVAL_NO_TIMEOUT); 1.224 + if (bytesRead < 0) { 1.225 + if (debug_mode) printf("\tServer error receiving data (%d)\n", bytesRead); 1.226 + continue; 1.227 + } 1.228 + if (debug_mode) DPRINTF("\tServer received %d bytes\n", bytesRead); 1.229 + } 1.230 + 1.231 + bytesWritten = PR_Send(newSock, 1.232 + sendBuf, 1.233 + bytesToWrite, 1.234 + 0, 1.235 + PR_INTERVAL_NO_TIMEOUT); 1.236 + if (bytesWritten != _server_data) { 1.237 + if (debug_mode) printf("\tError sending data to client (%d, %d)\n", 1.238 + bytesWritten, PR_GetOSError()); 1.239 + } else { 1.240 + if (debug_mode) DPRINTF("\tServer sent %d bytes\n", bytesWritten); 1.241 + } 1.242 + 1.243 + PR_Close(newSock); 1.244 + PR_AtomicDecrement(&workerThreadsBusy); 1.245 + } 1.246 +} 1.247 + 1.248 +PRFileDesc * 1.249 +ServerSetup(void) 1.250 +{ 1.251 + PRFileDesc *listenSocket; 1.252 + PRNetAddr serverAddr; 1.253 + PRThread *WorkerThread; 1.254 + 1.255 + if ( (listenSocket = PR_NewTCPSocket()) == NULL) { 1.256 + if (debug_mode) printf("\tServer error creating listen socket\n"); 1.257 + else Test_Result(FAIL); 1.258 + return NULL; 1.259 + } 1.260 + 1.261 + memset(&serverAddr, 0, sizeof(PRNetAddr)); 1.262 + serverAddr.inet.family = PR_AF_INET; 1.263 + serverAddr.inet.port = PR_htons(PORT); 1.264 + serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY); 1.265 + 1.266 + if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) { 1.267 + if (debug_mode) printf("\tServer error binding to server address: OS error %d\n", 1.268 + PR_GetOSError()); 1.269 + else Test_Result(FAIL); 1.270 + PR_Close(listenSocket); 1.271 + return NULL; 1.272 + } 1.273 + 1.274 + if ( PR_Listen(listenSocket, 128) == PR_FAILURE) { 1.275 + if (debug_mode) printf("\tServer error listening to server socket\n"); 1.276 + else Test_Result(FAIL); 1.277 + PR_Close(listenSocket); 1.278 + 1.279 + return NULL; 1.280 + } 1.281 + 1.282 + /* Create Clients */ 1.283 + workerThreads = 0; 1.284 + workerThreadsBusy = 0; 1.285 + 1.286 + workerThreadsLock = PR_NewLock(); 1.287 + 1.288 + WorkerThread = PR_CreateThread( 1.289 + PR_SYSTEM_THREAD, 1.290 + WorkerThreadFunc, 1.291 + listenSocket, 1.292 + PR_PRIORITY_NORMAL, 1.293 + ServerScope, 1.294 + PR_UNJOINABLE_THREAD, 1.295 + THREAD_STACKSIZE); 1.296 + 1.297 + if (!WorkerThread) { 1.298 + if (debug_mode) printf("error creating working thread\n"); 1.299 + PR_Close(listenSocket); 1.300 + return NULL; 1.301 + } 1.302 + PR_AtomicIncrement(&workerThreads); 1.303 + if (debug_mode) DPRINTF("\tServer created primordial worker thread\n"); 1.304 + 1.305 + return listenSocket; 1.306 +} 1.307 + 1.308 +/* The main server loop */ 1.309 +void 1.310 +ServerThreadFunc(void *unused) 1.311 +{ 1.312 + PRFileDesc *listenSocket; 1.313 + 1.314 + /* Do setup */ 1.315 + listenSocket = ServerSetup(); 1.316 + 1.317 + if (!listenSocket) { 1.318 + SetServerState(SERVER, SERVER_STATE_DEAD); 1.319 + } else { 1.320 + 1.321 + if (debug_mode) DPRINTF("\tServer up\n"); 1.322 + 1.323 + /* Tell clients they can start now. */ 1.324 + SetServerState(SERVER, SERVER_STATE_READY); 1.325 + 1.326 + /* Now wait for server death signal */ 1.327 + WaitServerState(SERVER, SERVER_STATE_DYING); 1.328 + 1.329 + /* Cleanup */ 1.330 + SetServerState(SERVER, SERVER_STATE_DEAD); 1.331 + } 1.332 +} 1.333 + 1.334 +/* --- Client Functions ------------------------------------------- */ 1.335 + 1.336 +PRInt32 numRequests; 1.337 +PRInt32 numClients; 1.338 +PRMonitor *clientMonitor; 1.339 + 1.340 +void 1.341 +ClientThreadFunc(void *unused) 1.342 +{ 1.343 + PRNetAddr serverAddr; 1.344 + PRFileDesc *clientSocket; 1.345 + char *sendBuf; 1.346 + char *recvBuf; 1.347 + PRInt32 rv; 1.348 + PRInt32 bytesNeeded; 1.349 + 1.350 + sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char)); 1.351 + if (!sendBuf) 1.352 + if (debug_mode) printf("\tClient could not malloc space!?\n"); 1.353 + recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char)); 1.354 + if (!recvBuf) 1.355 + if (debug_mode) printf("\tClient could not malloc space!?\n"); 1.356 + 1.357 + memset(&serverAddr, 0, sizeof(PRNetAddr)); 1.358 + serverAddr.inet.family = PR_AF_INET; 1.359 + serverAddr.inet.port = PR_htons(PORT); 1.360 + serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); 1.361 + 1.362 + while(numRequests > 0) { 1.363 + 1.364 + if ( (numRequests % 10) == 0 ) 1.365 + if (debug_mode) printf("."); 1.366 + if (debug_mode) DPRINTF("\tClient starting request %d\n", numRequests); 1.367 + 1.368 + clientSocket = PR_NewTCPSocket(); 1.369 + if (!clientSocket) { 1.370 + if (debug_mode) printf("Client error creating socket: OS error %d\n", 1.371 + PR_GetOSError()); 1.372 + continue; 1.373 + } 1.374 + 1.375 + if (debug_mode) DPRINTF("\tClient connecting\n"); 1.376 + 1.377 + rv = PR_Connect(clientSocket, 1.378 + &serverAddr, 1.379 + PR_INTERVAL_NO_TIMEOUT); 1.380 + if (!clientSocket) { 1.381 + if (debug_mode) printf("\tClient error connecting\n"); 1.382 + continue; 1.383 + } 1.384 + 1.385 + if (debug_mode) DPRINTF("\tClient connected\n"); 1.386 + 1.387 + rv = PR_Send(clientSocket, 1.388 + sendBuf, 1.389 + _client_data, 1.390 + 0, 1.391 + PR_INTERVAL_NO_TIMEOUT); 1.392 + if (rv != _client_data) { 1.393 + if (debug_mode) printf("Client error sending data (%d)\n", rv); 1.394 + PR_Close(clientSocket); 1.395 + continue; 1.396 + } 1.397 + 1.398 + if (debug_mode) DPRINTF("\tClient sent %d bytes\n", rv); 1.399 + 1.400 + bytesNeeded = _server_data; 1.401 + while(bytesNeeded) { 1.402 + rv = PR_Recv(clientSocket, 1.403 + recvBuf, 1.404 + bytesNeeded, 1.405 + 0, 1.406 + PR_INTERVAL_NO_TIMEOUT); 1.407 + if (rv <= 0) { 1.408 + if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n", 1.409 + rv, (_server_data - bytesNeeded), _server_data); 1.410 + break; 1.411 + } 1.412 + if (debug_mode) DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv); 1.413 + bytesNeeded -= rv; 1.414 + } 1.415 + 1.416 + PR_Close(clientSocket); 1.417 + 1.418 + PR_AtomicDecrement(&numRequests); 1.419 + } 1.420 + 1.421 + PR_EnterMonitor(clientMonitor); 1.422 + --numClients; 1.423 + PR_Notify(clientMonitor); 1.424 + PR_ExitMonitor(clientMonitor); 1.425 + 1.426 + PR_DELETE(sendBuf); 1.427 + PR_DELETE(recvBuf); 1.428 +} 1.429 + 1.430 +void 1.431 +RunClients(void) 1.432 +{ 1.433 + PRInt32 index; 1.434 + 1.435 + numRequests = _iterations; 1.436 + numClients = _clients; 1.437 + clientMonitor = PR_NewMonitor(); 1.438 + 1.439 + for (index=0; index<_clients; index++) { 1.440 + PRThread *clientThread; 1.441 + 1.442 + 1.443 + clientThread = PR_CreateThread( 1.444 + PR_USER_THREAD, 1.445 + ClientThreadFunc, 1.446 + NULL, 1.447 + PR_PRIORITY_NORMAL, 1.448 + ClientScope, 1.449 + PR_UNJOINABLE_THREAD, 1.450 + THREAD_STACKSIZE); 1.451 + 1.452 + if (!clientThread) { 1.453 + if (debug_mode) printf("\terror creating client thread %d\n", index); 1.454 + } else 1.455 + if (debug_mode) DPRINTF("\tMain created client %d/%d\n", index+1, _clients); 1.456 + 1.457 + } 1.458 + 1.459 + PR_EnterMonitor(clientMonitor); 1.460 + while(numClients) 1.461 + PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT); 1.462 + PR_ExitMonitor(clientMonitor); 1.463 +} 1.464 + 1.465 +/* --- Main Function ---------------------------------------------- */ 1.466 + 1.467 +static 1.468 +void do_work() 1.469 +{ 1.470 + PRThread *ServerThread; 1.471 + PRInt32 state; 1.472 + 1.473 + SetServerState(MAIN, SERVER_STATE_STARTUP); 1.474 + ServerThread = PR_CreateThread( 1.475 + PR_USER_THREAD, 1.476 + ServerThreadFunc, 1.477 + NULL, 1.478 + PR_PRIORITY_NORMAL, 1.479 + ServerScope, 1.480 + PR_JOINABLE_THREAD, 1.481 + THREAD_STACKSIZE); 1.482 + if (!ServerThread) { 1.483 + if (debug_mode) printf("error creating main server thread\n"); 1.484 + return; 1.485 + } 1.486 + 1.487 + /* Wait for server to be ready */ 1.488 + state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD); 1.489 + 1.490 + if (!(state & SERVER_STATE_DEAD)) { 1.491 + /* Run Test Clients */ 1.492 + RunClients(); 1.493 + 1.494 + /* Send death signal to server */ 1.495 + SetServerState(MAIN, SERVER_STATE_DYING); 1.496 + } 1.497 + 1.498 + PR_JoinThread(ServerThread); 1.499 +} 1.500 + 1.501 +static void do_workUU(void) 1.502 +{ 1.503 + ServerScope = PR_LOCAL_THREAD; 1.504 + ClientScope = PR_LOCAL_THREAD; 1.505 + do_work(); 1.506 +} 1.507 + 1.508 +static void do_workUK(void) 1.509 +{ 1.510 + ServerScope = PR_LOCAL_THREAD; 1.511 + ClientScope = PR_GLOBAL_THREAD; 1.512 + do_work(); 1.513 +} 1.514 + 1.515 +static void do_workKU(void) 1.516 +{ 1.517 + ServerScope = PR_GLOBAL_THREAD; 1.518 + ClientScope = PR_LOCAL_THREAD; 1.519 + do_work(); 1.520 +} 1.521 + 1.522 +static void do_workKK(void) 1.523 +{ 1.524 + ServerScope = PR_GLOBAL_THREAD; 1.525 + ClientScope = PR_GLOBAL_THREAD; 1.526 + do_work(); 1.527 +} 1.528 + 1.529 + 1.530 +static void Measure(void (*func)(void), const char *msg) 1.531 +{ 1.532 + PRIntervalTime start, stop; 1.533 + double d; 1.534 + 1.535 + start = PR_IntervalNow(); 1.536 + (*func)(); 1.537 + stop = PR_IntervalNow(); 1.538 + 1.539 + d = (double)PR_IntervalToMicroseconds(stop - start); 1.540 + 1.541 + if (debug_mode) printf("\n%40s: %6.2f usec\n", msg, d / _iterations); 1.542 +} 1.543 + 1.544 + 1.545 +int main(int argc, char **argv) 1.546 +{ 1.547 + /* The command line argument: -d is used to determine if the test is being run 1.548 + in debug mode. The regress tool requires only one line output:PASS or FAIL. 1.549 + All of the printfs associated with this test has been handled with a if (debug_mode) 1.550 + test. 1.551 + Usage: test_name -d 1.552 + */ 1.553 + PLOptStatus os; 1.554 + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); 1.555 + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 1.556 + { 1.557 + if (PL_OPT_BAD == os) continue; 1.558 + switch (opt->option) 1.559 + { 1.560 + case 'd': /* debug mode */ 1.561 + debug_mode = 1; 1.562 + break; 1.563 + default: 1.564 + break; 1.565 + } 1.566 + } 1.567 + PL_DestroyOptState(opt); 1.568 + 1.569 + /* main test */ 1.570 +#ifndef SYMBIAN 1.571 + if (debug_mode) { 1.572 + printf("Enter number of iterations: \n"); 1.573 + scanf("%d", &_iterations); 1.574 + printf("Enter number of clients : \n"); 1.575 + scanf("%d", &_clients); 1.576 + printf("Enter size of client data : \n"); 1.577 + scanf("%d", &_client_data); 1.578 + printf("Enter size of server data : \n"); 1.579 + scanf("%d", &_server_data); 1.580 + } 1.581 + else 1.582 +#endif 1.583 + { 1.584 + 1.585 + _iterations = 10; 1.586 + _clients = 1; 1.587 + _client_data = 10; 1.588 + _server_data = 10; 1.589 + } 1.590 + 1.591 + if (debug_mode) { 1.592 + printf("\n\n%d iterations with %d client threads.\n", 1.593 + _iterations, _clients); 1.594 + printf("Sending %d bytes of client data and %d bytes of server data\n", 1.595 + _client_data, _server_data); 1.596 + } 1.597 + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 1.598 + PR_STDIO_INIT(); 1.599 + 1.600 + ServerStateCVLock = PR_NewLock(); 1.601 + ServerStateCV = PR_NewCondVar(ServerStateCVLock); 1.602 + 1.603 + Measure(do_workUU, "server loop user/user"); 1.604 + #if 0 1.605 + Measure(do_workUK, "server loop user/kernel"); 1.606 + Measure(do_workKU, "server loop kernel/user"); 1.607 + Measure(do_workKK, "server loop kernel/kernel"); 1.608 + #endif 1.609 + 1.610 + PR_Cleanup(); 1.611 + 1.612 + return 0; 1.613 +}