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