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