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