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