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  * Notes:
9  * [1] lth. The call to Sleep() is a hack to get the test case to run
10  * on Windows 95. Without it, the test case fails with an error
11  * WSAECONNRESET following a recv() call. The error is caused by the
12  * server side thread termination without a shutdown() or closesocket()
13  * call. Windows docmunentation suggests that this is predicted
14  * behavior; that other platforms get away with it is ... serindipity.
15  * The test case should shutdown() or closesocket() before
16  * thread termination. I didn't have time to figure out where or how
17  * to do it. The Sleep() call inserts enough delay to allow the
18  * client side to recv() all his data before the server side thread
19  * terminates. Whew! ...
20  *
21  ** Modification History:
22  * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
23  *             The debug mode will print all of the printfs associated with this test.
24  *             The regress mode will be the default mode. Since the regress tool limits
25  *           the output to a one line status:PASS or FAIL,all of the printf statements
26  *             have been handled with an if (debug_mode) statement.
27  */
28 
29 #include "prclist.h"
30 #include "prcvar.h"
31 #include "prerror.h"
32 #include "prinit.h"
33 #include "prinrval.h"
34 #include "prio.h"
35 #include "prlock.h"
36 #include "prlog.h"
37 #include "prtime.h"
38 #include "prmem.h"
39 #include "prnetdb.h"
40 #include "prprf.h"
41 #include "prthread.h"
42 
43 #include "pprio.h"
44 #include "primpl.h"
45 
46 #include "plstr.h"
47 #include "plerror.h"
48 #include "plgetopt.h"
49 
50 #include <stdlib.h>
51 #include <string.h>
52 
53 #if defined(XP_UNIX)
54 #include <math.h>
55 #endif
56 
57 /*
58 ** This is the beginning of the test
59 */
60 
61 #define RECV_FLAGS 0
62 #define SEND_FLAGS 0
63 #define BUFFER_SIZE 1024
64 #define DEFAULT_BACKLOG 5
65 #define DEFAULT_PORT 13000
66 #define DEFAULT_CLIENTS 1
67 #define ALLOWED_IN_ACCEPT 1
68 #define DEFAULT_CLIPPING 1000
69 #define DEFAULT_WORKERS_MIN 1
70 #define DEFAULT_WORKERS_MAX 1
71 #define DEFAULT_SERVER "localhost"
72 #define DEFAULT_EXECUTION_TIME 10
73 #define DEFAULT_CLIENT_TIMEOUT 4000
74 #define DEFAULT_SERVER_TIMEOUT 4000
75 #define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH
76 
77 typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t;
78 
79 static void PR_CALLBACK Worker(void *arg);
80 typedef struct CSPool_s CSPool_t;
81 typedef struct CSWorker_s CSWorker_t;
82 typedef struct CSServer_s CSServer_t;
83 typedef enum Verbosity
84 {
85     TEST_LOG_ALWAYS,
86     TEST_LOG_ERROR,
87     TEST_LOG_WARNING,
88     TEST_LOG_NOTICE,
89     TEST_LOG_INFO,
90     TEST_LOG_STATUS,
91     TEST_LOG_VERBOSE
92 } Verbosity;
93 
94 static enum {
95     thread_nspr, thread_pthread, thread_sproc, thread_win32
96 } thread_provider;
97 
98 static PRInt32 domain = AF_INET;
99 static PRInt32 protocol = 6;  /* TCP */
100 static PRFileDesc *debug_out = NULL;
101 static PRBool debug_mode = PR_FALSE;
102 static PRBool pthread_stats = PR_FALSE;
103 static Verbosity verbosity = TEST_LOG_ALWAYS;
104 static PRThreadScope thread_scope = PR_LOCAL_THREAD;
105 
106 struct CSWorker_s
107 {
108     PRCList element;        /* list of the server's workers */
109 
110     PRThread *thread;       /* this worker objects thread */
111     CSServer_t *server;     /* back pointer to server structure */
112 };
113 
114 struct CSPool_s
115 {
116     PRCondVar *exiting;
117     PRCondVar *acceptComplete;
118     PRUint32 accepting, active, workers;
119 };
120 
121 struct CSServer_s
122 {
123     PRCList list;           /* head of worker list */
124 
125     PRLock *ml;
126     PRThread *thread;       /* the main server thread */
127     PRCondVar *stateChange;
128 
129     PRUint16 port;          /* port we're listening on */
130     PRUint32 backlog;       /* size of our listener backlog */
131     PRFileDesc *listener;   /* the fd accepting connections */
132 
133     CSPool_t pool;          /* statistics on worker threads */
134     CSState_t state;        /* the server's state */
135     struct                  /* controlling worker counts */
136     {
137         PRUint32 minimum, maximum, accepting;
138     } workers;
139 
140     /* statistics */
141     PRIntervalTime started, stopped;
142     PRUint32 operations, bytesTransferred;
143 };
144 
145 typedef struct CSDescriptor_s
146 {
147     PRInt32 size;       /* size of transfer */
148     char filename[60];  /* filename, null padded */
149 } CSDescriptor_t;
150 
151 typedef struct CSClient_s
152 {
153     PRLock *ml;
154     PRThread *thread;
155     PRCondVar *stateChange;
156     PRNetAddr serverAddress;
157 
158     CSState_t state;
159 
160     /* statistics */
161     PRIntervalTime started, stopped;
162     PRUint32 operations, bytesTransferred;
163 } CSClient_t;
164 
165 #define TEST_LOG(l, p, a) \
166     do { \
167         if (debug_mode || (p <= verbosity)) printf a; \
168     } while (0)
169 
170 PRLogModuleInfo *cltsrv_log_file = NULL;
171 
172 #define MY_ASSERT(_expr) \
173     ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
174 
175 #define TEST_ASSERT(_expr) \
176     ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
177 
_MY_Assert(const char * s,const char * file,PRIntn ln)178 static void _MY_Assert(const char *s, const char *file, PRIntn ln)
179 {
180     PL_PrintError(NULL);
181     PR_Assert(s, file, ln);
182 }  /* _MY_Assert */
183 
Aborted(PRStatus rv)184 static PRBool Aborted(PRStatus rv)
185 {
186     return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ?
187         PR_TRUE : PR_FALSE;
188 }
189 
TimeOfDayMessage(const char * msg,PRThread * me)190 static void TimeOfDayMessage(const char *msg, PRThread* me)
191 {
192     char buffer[100];
193     PRExplodedTime tod;
194     PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod);
195     (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod);
196 
197     TEST_LOG(
198         cltsrv_log_file, TEST_LOG_ALWAYS,
199         ("%s(0x%p): %s\n", msg, me, buffer));
200 }  /* TimeOfDayMessage */
201 
202 
Client(void * arg)203 static void PR_CALLBACK Client(void *arg)
204 {
205     PRStatus rv;
206     PRIntn index;
207     char buffer[1024];
208     PRFileDesc *fd = NULL;
209     PRUintn clipping = DEFAULT_CLIPPING;
210     CSClient_t *client = (CSClient_t*)arg;
211     PRThread *me = client->thread = PR_GetCurrentThread();
212     CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
213     PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT);
214 
215 
216     for (index = 0; index < sizeof(buffer); ++index)
217         buffer[index] = (char)index;
218 
219     client->started = PR_IntervalNow();
220 
221     PR_Lock(client->ml);
222     client->state = cs_run;
223     PR_NotifyCondVar(client->stateChange);
224     PR_Unlock(client->ml);
225 
226     TimeOfDayMessage("Client started at", me);
227 
228     while (cs_run == client->state)
229     {
230         PRInt32 bytes, descbytes, filebytes, netbytes;
231 
232         (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer));
233         TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
234             ("\tClient(0x%p): connecting to server at %s\n", me, buffer));
235 
236         fd = PR_Socket(domain, SOCK_STREAM, protocol);
237         TEST_ASSERT(NULL != fd);
238         rv = PR_Connect(fd, &client->serverAddress, timeout);
239         if (PR_FAILURE == rv)
240         {
241             TEST_LOG(
242                 cltsrv_log_file, TEST_LOG_ERROR,
243                 ("\tClient(0x%p): conection failed\n", me));
244             goto aborted;
245         }
246 
247         memset(descriptor, 0, sizeof(*descriptor));
248         descriptor->size = PR_htonl(descbytes = rand() % clipping);
249         PR_snprintf(
250             descriptor->filename, sizeof(descriptor->filename),
251             "CS%p%p-%p.dat", client->started, me, client->operations);
252         TEST_LOG(
253             cltsrv_log_file, TEST_LOG_VERBOSE,
254             ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes));
255         bytes = PR_Send(
256             fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout);
257         if (sizeof(CSDescriptor_t) != bytes)
258         {
259             if (Aborted(PR_FAILURE)) goto aborted;
260             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
261             {
262                 TEST_LOG(
263                     cltsrv_log_file, TEST_LOG_ERROR,
264                     ("\tClient(0x%p): send descriptor timeout\n", me));
265                 goto retry;
266             }
267         }
268         TEST_ASSERT(sizeof(*descriptor) == bytes);
269 
270         netbytes = 0;
271         while (netbytes < descbytes)
272         {
273             filebytes = sizeof(buffer);
274             if ((descbytes - netbytes) < filebytes)
275                 filebytes = descbytes - netbytes;
276             TEST_LOG(
277                 cltsrv_log_file, TEST_LOG_VERBOSE,
278                 ("\tClient(0x%p): sending %d bytes\n", me, filebytes));
279             bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
280             if (filebytes != bytes)
281             {
282                 if (Aborted(PR_FAILURE)) goto aborted;
283                 if (PR_IO_TIMEOUT_ERROR == PR_GetError())
284                 {
285                     TEST_LOG(
286                         cltsrv_log_file, TEST_LOG_ERROR,
287                         ("\tClient(0x%p): send data timeout\n", me));
288                     goto retry;
289                 }
290             }
291             TEST_ASSERT(bytes == filebytes);
292             netbytes += bytes;
293         }
294         filebytes = 0;
295         while (filebytes < descbytes)
296         {
297             netbytes = sizeof(buffer);
298             if ((descbytes - filebytes) < netbytes)
299                 netbytes = descbytes - filebytes;
300             TEST_LOG(
301                 cltsrv_log_file, TEST_LOG_VERBOSE,
302                 ("\tClient(0x%p): receiving %d bytes\n", me, netbytes));
303             bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
304             if (-1 == bytes)
305             {
306                 if (Aborted(PR_FAILURE))
307                 {
308                     TEST_LOG(
309                         cltsrv_log_file, TEST_LOG_ERROR,
310                         ("\tClient(0x%p): receive data aborted\n", me));
311                     goto aborted;
312                 }
313                 else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
314                     TEST_LOG(
315                         cltsrv_log_file, TEST_LOG_ERROR,
316                         ("\tClient(0x%p): receive data timeout\n", me));
317 				else
318                     TEST_LOG(
319                         cltsrv_log_file, TEST_LOG_ERROR,
320                         ("\tClient(0x%p): receive error (%d, %d)\n",
321 						me, PR_GetError(), PR_GetOSError()));
322                 goto retry;
323            }
324             if (0 == bytes)
325             {
326                 TEST_LOG(
327                     cltsrv_log_file, TEST_LOG_ERROR,
328                     ("\t\tClient(0x%p): unexpected end of stream\n",
329                     PR_GetCurrentThread()));
330                 break;
331             }
332             filebytes += bytes;
333         }
334 
335         rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
336         if (Aborted(rv)) goto aborted;
337         TEST_ASSERT(PR_SUCCESS == rv);
338 retry:
339         (void)PR_Close(fd); fd = NULL;
340         TEST_LOG(
341             cltsrv_log_file, TEST_LOG_INFO,
342             ("\tClient(0x%p): disconnected from server\n", me));
343 
344         PR_Lock(client->ml);
345         client->operations += 1;
346         client->bytesTransferred += 2 * descbytes;
347         rv = PR_WaitCondVar(client->stateChange, rand() % clipping);
348         PR_Unlock(client->ml);
349         if (Aborted(rv)) break;
350     }
351 
352 aborted:
353     client->stopped = PR_IntervalNow();
354 
355     PR_ClearInterrupt();
356     if (NULL != fd) rv = PR_Close(fd);
357 
358     PR_Lock(client->ml);
359     client->state = cs_exit;
360     PR_NotifyCondVar(client->stateChange);
361     PR_Unlock(client->ml);
362     PR_DELETE(descriptor);
363     TEST_LOG(
364         cltsrv_log_file, TEST_LOG_ALWAYS,
365         ("\tClient(0x%p): stopped after %u operations and %u bytes\n",
366         PR_GetCurrentThread(), client->operations, client->bytesTransferred));
367 
368 }  /* Client */
369 
ProcessRequest(PRFileDesc * fd,CSServer_t * server)370 static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server)
371 {
372     PRStatus drv, rv;
373     char buffer[1024];
374     PRFileDesc *file = NULL;
375     PRThread * me = PR_GetCurrentThread();
376     PRInt32 bytes, descbytes, netbytes, filebytes = 0;
377     CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
378     PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT);
379 
380     TEST_LOG(
381         cltsrv_log_file, TEST_LOG_VERBOSE,
382         ("\tProcessRequest(0x%p): receiving desciptor\n", me));
383     bytes = PR_Recv(
384         fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout);
385     if (-1 == bytes)
386     {
387         rv = PR_FAILURE;
388         if (Aborted(rv)) goto exit;
389         if (PR_IO_TIMEOUT_ERROR == PR_GetError())
390         {
391             TEST_LOG(
392                 cltsrv_log_file, TEST_LOG_ERROR,
393                 ("\tProcessRequest(0x%p): receive timeout\n", me));
394         }
395         goto exit;
396     }
397     if (0 == bytes)
398     {
399         rv = PR_FAILURE;
400         TEST_LOG(
401             cltsrv_log_file, TEST_LOG_ERROR,
402             ("\tProcessRequest(0x%p): unexpected end of file\n", me));
403         goto exit;
404     }
405     descbytes = PR_ntohl(descriptor->size);
406     TEST_ASSERT(sizeof(*descriptor) == bytes);
407 
408     TEST_LOG(
409         cltsrv_log_file, TEST_LOG_VERBOSE,
410         ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n",
411         me, descbytes, descriptor->filename));
412 
413     file = PR_Open(
414         descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666);
415     if (NULL == file)
416     {
417         rv = PR_FAILURE;
418         if (Aborted(rv)) goto aborted;
419         if (PR_IO_TIMEOUT_ERROR == PR_GetError())
420         {
421             TEST_LOG(
422                 cltsrv_log_file, TEST_LOG_ERROR,
423                 ("\tProcessRequest(0x%p): open file timeout\n", me));
424             goto aborted;
425         }
426     }
427     TEST_ASSERT(NULL != file);
428 
429     filebytes = 0;
430     while (filebytes < descbytes)
431     {
432         netbytes = sizeof(buffer);
433         if ((descbytes - filebytes) < netbytes)
434             netbytes = descbytes - filebytes;
435         TEST_LOG(
436             cltsrv_log_file, TEST_LOG_VERBOSE,
437             ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes));
438         bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
439         if (-1 == bytes)
440         {
441             rv = PR_FAILURE;
442             if (Aborted(rv)) goto aborted;
443             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
444             {
445                 TEST_LOG(
446                     cltsrv_log_file, TEST_LOG_ERROR,
447                     ("\t\tProcessRequest(0x%p): receive data timeout\n", me));
448                 goto aborted;
449             }
450             /*
451              * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
452              * on NT here.  This is equivalent to ECONNRESET on Unix.
453              *     -wtc
454              */
455             TEST_LOG(
456                 cltsrv_log_file, TEST_LOG_WARNING,
457                 ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n",
458                 me, PR_GetError(), PR_GetOSError()));
459             goto aborted;
460         }
461         if(0 == bytes)
462         {
463             TEST_LOG(
464                 cltsrv_log_file, TEST_LOG_WARNING,
465                 ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
466             rv = PR_FAILURE;
467             goto aborted;
468         }
469         filebytes += bytes;
470         netbytes = bytes;
471         /* The byte count for PR_Write should be positive */
472         MY_ASSERT(netbytes > 0);
473         TEST_LOG(
474             cltsrv_log_file, TEST_LOG_VERBOSE,
475             ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes));
476         bytes = PR_Write(file, buffer, netbytes);
477         if (netbytes != bytes)
478         {
479             rv = PR_FAILURE;
480             if (Aborted(rv)) goto aborted;
481             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
482             {
483                 TEST_LOG(
484                     cltsrv_log_file, TEST_LOG_ERROR,
485                     ("\t\tProcessRequest(0x%p): write file timeout\n", me));
486                 goto aborted;
487             }
488         }
489         TEST_ASSERT(bytes > 0);
490     }
491 
492     PR_Lock(server->ml);
493     server->operations += 1;
494     server->bytesTransferred += filebytes;
495     PR_Unlock(server->ml);
496 
497     rv = PR_Close(file); file = NULL;
498     if (Aborted(rv)) goto aborted;
499     TEST_ASSERT(PR_SUCCESS == rv);
500 
501     TEST_LOG(
502         cltsrv_log_file, TEST_LOG_VERBOSE,
503         ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename));
504     file = PR_Open(descriptor->filename, PR_RDONLY, 0);
505     if (NULL == file)
506     {
507         rv = PR_FAILURE;
508         if (Aborted(rv)) goto aborted;
509         if (PR_IO_TIMEOUT_ERROR == PR_GetError())
510         {
511             TEST_LOG(
512                 cltsrv_log_file, TEST_LOG_ERROR,
513                 ("\t\tProcessRequest(0x%p): open file timeout\n",
514                 PR_GetCurrentThread()));
515             goto aborted;
516         }
517         TEST_LOG(
518             cltsrv_log_file, TEST_LOG_ERROR,
519             ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n",
520             me, PR_GetError(), PR_GetOSError()));
521         goto aborted;
522     }
523     TEST_ASSERT(NULL != file);
524 
525     netbytes = 0;
526     while (netbytes < descbytes)
527     {
528         filebytes = sizeof(buffer);
529         if ((descbytes - netbytes) < filebytes)
530             filebytes = descbytes - netbytes;
531         TEST_LOG(
532             cltsrv_log_file, TEST_LOG_VERBOSE,
533             ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes));
534         bytes = PR_Read(file, buffer, filebytes);
535         if (filebytes != bytes)
536         {
537             rv = PR_FAILURE;
538             if (Aborted(rv)) goto aborted;
539             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
540                 TEST_LOG(
541                     cltsrv_log_file, TEST_LOG_ERROR,
542                     ("\t\tProcessRequest(0x%p): read file timeout\n", me));
543             else
544                 TEST_LOG(
545                     cltsrv_log_file, TEST_LOG_ERROR,
546                     ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n",
547                     me, PR_GetError(), PR_GetOSError()));
548             goto aborted;
549         }
550         TEST_ASSERT(bytes > 0);
551         netbytes += bytes;
552         filebytes = bytes;
553         TEST_LOG(
554             cltsrv_log_file, TEST_LOG_VERBOSE,
555             ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes));
556         bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
557         if (filebytes != bytes)
558         {
559             rv = PR_FAILURE;
560             if (Aborted(rv)) goto aborted;
561             if (PR_IO_TIMEOUT_ERROR == PR_GetError())
562             {
563                 TEST_LOG(
564                     cltsrv_log_file, TEST_LOG_ERROR,
565                     ("\t\tProcessRequest(0x%p): send data timeout\n", me));
566                 goto aborted;
567             }
568             break;
569         }
570        TEST_ASSERT(bytes > 0);
571     }
572 
573     PR_Lock(server->ml);
574     server->bytesTransferred += filebytes;
575     PR_Unlock(server->ml);
576 
577     rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
578     if (Aborted(rv)) goto aborted;
579 
580     rv = PR_Close(file); file = NULL;
581     if (Aborted(rv)) goto aborted;
582     TEST_ASSERT(PR_SUCCESS == rv);
583 
584 aborted:
585     PR_ClearInterrupt();
586     if (NULL != file) PR_Close(file);
587     drv = PR_Delete(descriptor->filename);
588     TEST_ASSERT(PR_SUCCESS == drv);
589 exit:
590     TEST_LOG(
591         cltsrv_log_file, TEST_LOG_VERBOSE,
592         ("\t\tProcessRequest(0x%p): Finished\n", me));
593 
594     PR_DELETE(descriptor);
595 
596 #if defined(WIN95)
597     PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */
598 #endif
599     return rv;
600 }  /* ProcessRequest */
601 
602 typedef void (*StartFn)(void*);
603 typedef struct StartObject
604 {
605     StartFn start;
606     void *arg;
607 } StartObject;
608 
609 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
610 #include "md/_pth.h"
611 #include <pthread.h>
612 
pthread_start(void * arg)613 static void *pthread_start(void *arg)
614 {
615     StartObject *so = (StartObject*)arg;
616     StartFn start = so->start;
617     void *data = so->arg;
618     PR_Free(so);
619     start(data);
620     return NULL;
621 }  /* pthread_start */
622 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
623 
624 #if defined(IRIX) && !defined(_PR_PTHREADS)
625 #include <sys/types.h>
626 #include <sys/prctl.h>
sproc_start(void * arg,PRSize size)627 static void sproc_start(void *arg, PRSize size)
628 {
629     StartObject *so = (StartObject*)arg;
630     StartFn start = so->start;
631     void *data = so->arg;
632     PR_Free(so);
633     start(data);
634 }  /* sproc_start */
635 #endif  /* defined(IRIX) && !defined(_PR_PTHREADS) */
636 
637 #if defined(WIN32)
638 #include <process.h>  /* for _beginthreadex() */
639 
windows_start(void * arg)640 static PRUintn __stdcall windows_start(void *arg)
641 {
642     StartObject *so = (StartObject*)arg;
643     StartFn start = so->start;
644     void *data = so->arg;
645     PR_Free(so);
646     start(data);
647     return 0;
648 }  /* windows_start */
649 #endif /* defined(WIN32) */
650 
JoinThread(PRThread * thread)651 static PRStatus JoinThread(PRThread *thread)
652 {
653     PRStatus rv;
654     switch (thread_provider)
655     {
656     case thread_nspr:
657         rv = PR_JoinThread(thread);
658         break;
659     case thread_pthread:
660 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
661         rv = PR_SUCCESS;
662         break;
663 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
664     case thread_win32:
665 #if defined(WIN32)
666         rv = PR_SUCCESS;
667         break;
668 #endif
669     default:
670         rv = PR_FAILURE;
671         break;
672     }
673     return rv;
674 }  /* JoinThread */
675 
NewThread(StartFn start,void * arg,PRThreadPriority prio,PRThreadState state)676 static PRStatus NewThread(
677     StartFn start, void *arg, PRThreadPriority prio, PRThreadState state)
678 {
679     PRStatus rv;
680 
681     switch (thread_provider)
682     {
683     case thread_nspr:
684         {
685             PRThread *thread = PR_CreateThread(
686                 PR_USER_THREAD, start, arg,
687                 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
688                 PR_JOINABLE_THREAD, 0);
689             rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS;
690         }
691         break;
692     case thread_pthread:
693 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
694         {
695             int rv;
696             pthread_t id;
697             pthread_attr_t tattr;
698             StartObject *start_object;
699             start_object = PR_NEW(StartObject);
700             PR_ASSERT(NULL != start_object);
701             start_object->start = start;
702             start_object->arg = arg;
703 
704             rv = _PT_PTHREAD_ATTR_INIT(&tattr);
705             PR_ASSERT(0 == rv);
706 
707             rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
708             PR_ASSERT(0 == rv);
709 
710             rv = pthread_attr_setstacksize(&tattr, 64 * 1024);
711             PR_ASSERT(0 == rv);
712 
713             rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object);
714             (void)_PT_PTHREAD_ATTR_DESTROY(&tattr);
715             return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
716         }
717 #else
718         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
719         rv = PR_FAILURE;
720 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
721         break;
722 
723     case thread_sproc:
724 #if defined(IRIX) && !defined(_PR_PTHREADS)
725         {
726             PRInt32 pid;
727             StartObject *start_object;
728             start_object = PR_NEW(StartObject);
729             PR_ASSERT(NULL != start_object);
730             start_object->start = start;
731             start_object->arg = arg;
732             pid = sprocsp(
733                 sproc_start, PR_SALL, start_object, NULL, 64 * 1024);
734             rv = (0 < pid) ? PR_SUCCESS : PR_FAILURE;
735         }
736 #else
737         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
738         rv = PR_FAILURE;
739 #endif  /* defined(IRIX) && !defined(_PR_PTHREADS) */
740         break;
741     case thread_win32:
742 #if defined(WIN32)
743         {
744             void *th;
745             PRUintn id;
746             StartObject *start_object;
747             start_object = PR_NEW(StartObject);
748             PR_ASSERT(NULL != start_object);
749             start_object->start = start;
750             start_object->arg = arg;
751             th = (void*)_beginthreadex(
752                 NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */
753                 0U, /* DWORD - initial thread stack size, in bytes */
754                 windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */
755                 start_object, /* LPVOID - argument for new thread */
756                 STACK_SIZE_PARAM_IS_A_RESERVATION, /*DWORD dwCreationFlags - creation flags */
757                 &id /* LPDWORD - pointer to returned thread identifier */ );
758 
759             rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS;
760         }
761 #else
762         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
763         rv = PR_FAILURE;
764 #endif
765         break;
766     default:
767         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
768         rv = PR_FAILURE;
769     }
770     return rv;
771 }  /* NewThread */
772 
CreateWorker(CSServer_t * server,CSPool_t * pool)773 static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool)
774 {
775     PRStatus rv;
776     CSWorker_t *worker = PR_NEWZAP(CSWorker_t);
777     worker->server = server;
778     PR_INIT_CLIST(&worker->element);
779     rv = NewThread(
780         Worker, worker, DEFAULT_SERVER_PRIORITY, PR_UNJOINABLE_THREAD);
781     if (PR_FAILURE == rv) PR_DELETE(worker);
782 
783     TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
784         ("\tCreateWorker(0x%p): create new worker (0x%p)\n",
785         PR_GetCurrentThread(), worker->thread));
786 
787     return rv;
788 }  /* CreateWorker */
789 
Worker(void * arg)790 static void PR_CALLBACK Worker(void *arg)
791 {
792     PRStatus rv;
793     PRNetAddr from;
794     PRFileDesc *fd = NULL;
795     CSWorker_t *worker = (CSWorker_t*)arg;
796     CSServer_t *server = worker->server;
797     CSPool_t *pool = &server->pool;
798 
799     PRThread *me = worker->thread = PR_GetCurrentThread();
800 
801     TEST_LOG(
802         cltsrv_log_file, TEST_LOG_NOTICE,
803         ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1));
804 
805     PR_Lock(server->ml);
806     PR_APPEND_LINK(&worker->element, &server->list);
807     pool->workers += 1;  /* define our existance */
808 
809     while (cs_run == server->state)
810     {
811         while (pool->accepting >= server->workers.accepting)
812         {
813             TEST_LOG(
814                 cltsrv_log_file, TEST_LOG_VERBOSE,
815                 ("\t\tWorker(0x%p): waiting for accept slot[%d]\n",
816                 me, pool->accepting));
817             rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT);
818             if (Aborted(rv) || (cs_run != server->state))
819             {
820                 TEST_LOG(
821                     cltsrv_log_file, TEST_LOG_NOTICE,
822                     ("\tWorker(0x%p): has been %s\n",
823                     me, (Aborted(rv) ? "interrupted" : "stopped")));
824                 goto exit;
825             }
826         }
827         pool->accepting += 1;  /* how many are really in accept */
828         PR_Unlock(server->ml);
829 
830         TEST_LOG(
831             cltsrv_log_file, TEST_LOG_VERBOSE,
832             ("\t\tWorker(0x%p): calling accept\n", me));
833         fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT);
834 
835         PR_Lock(server->ml);
836         pool->accepting -= 1;
837         PR_NotifyCondVar(pool->acceptComplete);
838 
839         if ((NULL == fd) && Aborted(PR_FAILURE))
840         {
841             if (NULL != server->listener)
842             {
843                 PR_Close(server->listener);
844                 server->listener = NULL;
845             }
846             goto exit;
847         }
848 
849         if (NULL != fd)
850         {
851             /*
852             ** Create another worker of the total number of workers is
853             ** less than the minimum specified or we have none left in
854             ** accept() AND we're not over the maximum.
855             ** This sort of presumes that the number allowed in accept
856             ** is at least as many as the minimum. Otherwise we'll keep
857             ** creating new threads and deleting them soon after.
858             */
859             PRBool another =
860                 ((pool->workers < server->workers.minimum) ||
861                 ((0 == pool->accepting)
862                     && (pool->workers < server->workers.maximum))) ?
863                     PR_TRUE : PR_FALSE;
864             pool->active += 1;
865             PR_Unlock(server->ml);
866 
867             if (another) (void)CreateWorker(server, pool);
868 
869             rv = ProcessRequest(fd, server);
870             if (PR_SUCCESS != rv)
871                 TEST_LOG(
872                     cltsrv_log_file, TEST_LOG_ERROR,
873                     ("\t\tWorker(0x%p): server process ended abnormally\n", me));
874             (void)PR_Close(fd); fd = NULL;
875 
876             PR_Lock(server->ml);
877             pool->active -= 1;
878         }
879     }
880 
881 exit:
882     PR_ClearInterrupt();
883     PR_Unlock(server->ml);
884 
885     if (NULL != fd)
886     {
887         (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
888         (void)PR_Close(fd);
889     }
890 
891     TEST_LOG(
892         cltsrv_log_file, TEST_LOG_NOTICE,
893         ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers));
894 
895     PR_Lock(server->ml);
896     pool->workers -= 1;  /* undefine our existance */
897     PR_REMOVE_AND_INIT_LINK(&worker->element);
898     PR_NotifyCondVar(pool->exiting);
899     PR_Unlock(server->ml);
900 
901     PR_DELETE(worker);  /* destruction of the "worker" object */
902 
903 }  /* Worker */
904 
Server(void * arg)905 static void PR_CALLBACK Server(void *arg)
906 {
907     PRStatus rv;
908     PRNetAddr serverAddress;
909     CSServer_t *server = (CSServer_t*)arg;
910     PRThread *me = server->thread = PR_GetCurrentThread();
911     PRSocketOptionData sockOpt;
912 
913     server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
914 
915     sockOpt.option = PR_SockOpt_Reuseaddr;
916     sockOpt.value.reuse_addr = PR_TRUE;
917     rv = PR_SetSocketOption(server->listener, &sockOpt);
918     TEST_ASSERT(PR_SUCCESS == rv);
919 
920     memset(&serverAddress, 0, sizeof(serverAddress));
921     rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
922 
923     rv = PR_Bind(server->listener, &serverAddress);
924     TEST_ASSERT(PR_SUCCESS == rv);
925 
926     rv = PR_Listen(server->listener, server->backlog);
927     TEST_ASSERT(PR_SUCCESS == rv);
928 
929     server->started = PR_IntervalNow();
930     TimeOfDayMessage("Server started at", me);
931 
932     PR_Lock(server->ml);
933     server->state = cs_run;
934     PR_NotifyCondVar(server->stateChange);
935     PR_Unlock(server->ml);
936 
937     /*
938     ** Create the first worker (actually, a thread that accepts
939     ** connections and then processes the work load as needed).
940     ** From this point on, additional worker threads are created
941     ** as they are needed by existing worker threads.
942     */
943     rv = CreateWorker(server, &server->pool);
944     TEST_ASSERT(PR_SUCCESS == rv);
945 
946     /*
947     ** From here on this thread is merely hanging around as the contact
948     ** point for the main test driver. It's just waiting for the driver
949     ** to declare the test complete.
950     */
951     TEST_LOG(
952         cltsrv_log_file, TEST_LOG_VERBOSE,
953         ("\tServer(0x%p): waiting for state change\n", me));
954 
955     PR_Lock(server->ml);
956     while ((cs_run == server->state) && !Aborted(rv))
957     {
958         rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
959     }
960     PR_Unlock(server->ml);
961     PR_ClearInterrupt();
962 
963     TEST_LOG(
964         cltsrv_log_file, TEST_LOG_INFO,
965         ("\tServer(0x%p): shutting down workers\n", me));
966 
967     /*
968     ** Get all the worker threads to exit. They know how to
969     ** clean up after themselves, so this is just a matter of
970     ** waiting for clorine in the pool to take effect. During
971     ** this stage we're ignoring interrupts.
972     */
973     server->workers.minimum = server->workers.maximum = 0;
974 
975     PR_Lock(server->ml);
976     while (!PR_CLIST_IS_EMPTY(&server->list))
977     {
978         PRCList *head = PR_LIST_HEAD(&server->list);
979         CSWorker_t *worker = (CSWorker_t*)head;
980         TEST_LOG(
981             cltsrv_log_file, TEST_LOG_VERBOSE,
982             ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker));
983         rv = PR_Interrupt(worker->thread);
984         TEST_ASSERT(PR_SUCCESS == rv);
985         PR_REMOVE_AND_INIT_LINK(head);
986     }
987 
988     while (server->pool.workers > 0)
989     {
990         TEST_LOG(
991             cltsrv_log_file, TEST_LOG_NOTICE,
992             ("\tServer(0x%p): waiting for %u workers to exit\n",
993             me, server->pool.workers));
994         (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT);
995     }
996 
997     server->state = cs_exit;
998     PR_NotifyCondVar(server->stateChange);
999     PR_Unlock(server->ml);
1000 
1001     TEST_LOG(
1002         cltsrv_log_file, TEST_LOG_ALWAYS,
1003         ("\tServer(0x%p): stopped after %u operations and %u bytes\n",
1004         me, server->operations, server->bytesTransferred));
1005 
1006     if (NULL != server->listener) PR_Close(server->listener);
1007     server->stopped = PR_IntervalNow();
1008 
1009 }  /* Server */
1010 
WaitForCompletion(PRIntn execution)1011 static void WaitForCompletion(PRIntn execution)
1012 {
1013     while (execution > 0)
1014     {
1015         PRIntn dally = (execution > 30) ? 30 : execution;
1016         PR_Sleep(PR_SecondsToInterval(dally));
1017         if (pthread_stats) PT_FPrintStats(debug_out, "\nPThread Statistics\n");
1018         execution -= dally;
1019     }
1020 }  /* WaitForCompletion */
1021 
Help(void)1022 static void Help(void)
1023 {
1024     PR_fprintf(debug_out, "cltsrv test program usage:\n");
1025     PR_fprintf(debug_out, "\t-a <n>       threads allowed in accept        (5)\n");
1026     PR_fprintf(debug_out, "\t-b <n>       backlock for listen              (5)\n");
1027     PR_fprintf(debug_out, "\t-c <threads> number of clients to create      (1)\n");
1028     PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n");
1029     PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n");
1030     PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds  (10)\n");
1031     PR_fprintf(debug_out, "\t-s <string>  dsn name of server               (localhost)\n");
1032     PR_fprintf(debug_out, "\t-G           use GLOBAL threads               (LOCAL)\n");
1033     PR_fprintf(debug_out, "\t-T <string>  thread provider ('n' | 'p' | 'w')(n)\n");
1034     PR_fprintf(debug_out, "\t-X           use XTP as transport             (TCP)\n");
1035     PR_fprintf(debug_out, "\t-6           Use IPv6                         (IPv4)\n");
1036     PR_fprintf(debug_out, "\t-v           verbosity (accumulative)         (0)\n");
1037     PR_fprintf(debug_out, "\t-p           pthread statistics               (FALSE)\n");
1038     PR_fprintf(debug_out, "\t-d           debug mode                       (FALSE)\n");
1039     PR_fprintf(debug_out, "\t-h           this message\n");
1040 }  /* Help */
1041 
IncrementVerbosity(void)1042 static Verbosity IncrementVerbosity(void)
1043 {
1044     PRIntn verboge = (PRIntn)verbosity + 1;
1045     return (Verbosity)verboge;
1046 }  /* IncrementVerbosity */
1047 
main(int argc,char ** argv)1048 int main(int argc, char **argv)
1049 {
1050     PRUintn index;
1051     PRBool boolean;
1052     CSClient_t *client;
1053     PRStatus rv, joinStatus;
1054     CSServer_t *server = NULL;
1055 	char *thread_type;
1056 
1057     PRUintn backlog = DEFAULT_BACKLOG;
1058     PRUintn clients = DEFAULT_CLIENTS;
1059     const char *serverName = DEFAULT_SERVER;
1060     PRBool serverIsLocal = PR_TRUE;
1061     PRUintn accepting = ALLOWED_IN_ACCEPT;
1062     PRUintn workersMin = DEFAULT_WORKERS_MIN;
1063     PRUintn workersMax = DEFAULT_WORKERS_MAX;
1064     PRIntn execution = DEFAULT_EXECUTION_TIME;
1065 
1066     /*
1067      * -G           use global threads
1068      * -a <n>       threads allowed in accept
1069      * -b <n>       backlock for listen
1070      * -c <threads> number of clients to create
1071      * -w <threads> minimal number of server threads
1072      * -W <threads> maximum number of server threads
1073      * -e <seconds> duration of the test in seconds
1074      * -s <string>  dsn name of server (implies no server here)
1075      * -v           verbosity
1076      */
1077 
1078     PLOptStatus os;
1079     PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:w:W:e:s:T:vdhp");
1080 
1081 #if defined(WIN32)
1082 	thread_provider = thread_win32;
1083 #elif defined(_PR_PTHREADS)
1084 	thread_provider = thread_pthread;
1085 #elif defined(IRIX)
1086 	thread_provider = thread_sproc;
1087 #else
1088     thread_provider = thread_nspr;
1089 #endif
1090 
1091     debug_out = PR_GetSpecialFD(PR_StandardError);
1092 
1093     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
1094     {
1095         if (PL_OPT_BAD == os) continue;
1096         switch (opt->option)
1097         {
1098         case 'G':  /* use global threads */
1099             thread_scope = PR_GLOBAL_THREAD;
1100             break;
1101         case 'X':  /* use XTP as transport */
1102             protocol = 36;
1103             break;
1104 		case '6':  /* Use IPv6 */
1105             domain = PR_AF_INET6;
1106             break;
1107         case 'a':  /* the value for accepting */
1108             accepting = atoi(opt->value);
1109             break;
1110         case 'b':  /* the value for backlock */
1111             backlog = atoi(opt->value);
1112             break;
1113         case 'T':  /* the thread provider */
1114             if ('n' == *opt->value) thread_provider = thread_nspr;
1115             else if ('p' == *opt->value) thread_provider = thread_pthread;
1116             else if ('w' == *opt->value) thread_provider = thread_win32;
1117             else {Help(); return 2; }
1118             break;
1119         case 'c':  /* number of client threads */
1120             clients = atoi(opt->value);
1121             break;
1122         case 'w':  /* minimum server worker threads */
1123             workersMin = atoi(opt->value);
1124             break;
1125         case 'W':  /* maximum server worker threads */
1126             workersMax = atoi(opt->value);
1127             break;
1128         case 'e':  /* program execution time in seconds */
1129             execution = atoi(opt->value);
1130             break;
1131         case 's':  /* server's address */
1132             serverName = opt->value;
1133             break;
1134         case 'v':  /* verbosity */
1135             verbosity = IncrementVerbosity();
1136             break;
1137         case 'd':  /* debug mode */
1138             debug_mode = PR_TRUE;
1139             break;
1140         case 'p':  /* pthread mode */
1141             pthread_stats = PR_TRUE;
1142             break;
1143         case 'h':
1144         default:
1145             Help();
1146             return 2;
1147         }
1148     }
1149     PL_DestroyOptState(opt);
1150 
1151     if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) serverIsLocal = PR_FALSE;
1152     if (0 == execution) execution = DEFAULT_EXECUTION_TIME;
1153     if (0 == workersMax) workersMax = DEFAULT_WORKERS_MAX;
1154     if (0 == workersMin) workersMin = DEFAULT_WORKERS_MIN;
1155     if (0 == accepting) accepting = ALLOWED_IN_ACCEPT;
1156     if (0 == backlog) backlog = DEFAULT_BACKLOG;
1157 
1158     if (workersMin > accepting) accepting = workersMin;
1159 
1160     PR_STDIO_INIT();
1161     TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread());
1162 
1163     cltsrv_log_file = PR_NewLogModule("cltsrv_log");
1164     MY_ASSERT(NULL != cltsrv_log_file);
1165     boolean = PR_SetLogFile("cltsrv.log");
1166     MY_ASSERT(boolean);
1167 
1168     if (serverIsLocal)
1169     {
1170         /* Establish the server */
1171         TEST_LOG(
1172             cltsrv_log_file, TEST_LOG_INFO,
1173             ("main(0x%p): starting server\n", PR_GetCurrentThread()));
1174 
1175         server = PR_NEWZAP(CSServer_t);
1176         PR_INIT_CLIST(&server->list);
1177         server->state = cs_init;
1178         server->ml = PR_NewLock();
1179         server->backlog = backlog;
1180         server->port = DEFAULT_PORT;
1181         server->workers.minimum = workersMin;
1182         server->workers.maximum = workersMax;
1183         server->workers.accepting = accepting;
1184         server->stateChange = PR_NewCondVar(server->ml);
1185         server->pool.exiting = PR_NewCondVar(server->ml);
1186         server->pool.acceptComplete = PR_NewCondVar(server->ml);
1187 
1188         TEST_LOG(
1189             cltsrv_log_file, TEST_LOG_NOTICE,
1190             ("main(0x%p): creating server thread\n", PR_GetCurrentThread()));
1191 
1192         rv = NewThread(
1193             Server, server, PR_PRIORITY_HIGH, PR_JOINABLE_THREAD);
1194         TEST_ASSERT(PR_SUCCESS == rv);
1195 
1196         TEST_LOG(
1197             cltsrv_log_file, TEST_LOG_VERBOSE,
1198             ("main(0x%p): waiting for server init\n", PR_GetCurrentThread()));
1199 
1200         PR_Lock(server->ml);
1201         while (server->state == cs_init)
1202             PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
1203         PR_Unlock(server->ml);
1204 
1205         TEST_LOG(
1206             cltsrv_log_file, TEST_LOG_VERBOSE,
1207             ("main(0x%p): server init complete (port #%d)\n",
1208             PR_GetCurrentThread(), server->port));
1209     }
1210 
1211     if (clients != 0)
1212     {
1213         /* Create all of the clients */
1214         PRHostEnt host;
1215         char buffer[BUFFER_SIZE];
1216         client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t));
1217 
1218         TEST_LOG(
1219             cltsrv_log_file, TEST_LOG_VERBOSE,
1220             ("main(0x%p): creating %d client threads\n",
1221             PR_GetCurrentThread(), clients));
1222 
1223         if (!serverIsLocal)
1224         {
1225             rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host);
1226             if (PR_SUCCESS != rv)
1227             {
1228                 PL_FPrintError(PR_STDERR, "PR_GetHostByName");
1229                 return 2;
1230             }
1231         }
1232 
1233         for (index = 0; index < clients; ++index)
1234         {
1235             client[index].state = cs_init;
1236             client[index].ml = PR_NewLock();
1237             if (serverIsLocal)
1238             {
1239                 (void)PR_InitializeNetAddr(
1240                     PR_IpAddrLoopback, DEFAULT_PORT,
1241                     &client[index].serverAddress);
1242             }
1243             else
1244             {
1245                 (void)PR_EnumerateHostEnt(
1246                     0, &host, DEFAULT_PORT, &client[index].serverAddress);
1247             }
1248             client[index].stateChange = PR_NewCondVar(client[index].ml);
1249             TEST_LOG(
1250                 cltsrv_log_file, TEST_LOG_INFO,
1251                 ("main(0x%p): creating client threads\n", PR_GetCurrentThread()));
1252             rv = NewThread(
1253                 Client, &client[index], PR_PRIORITY_NORMAL, PR_JOINABLE_THREAD);
1254             TEST_ASSERT(PR_SUCCESS == rv);
1255             PR_Lock(client[index].ml);
1256             while (cs_init == client[index].state)
1257                 PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
1258             PR_Unlock(client[index].ml);
1259         }
1260     }
1261 
1262     /* Then just let them go at it for a bit */
1263     TEST_LOG(
1264         cltsrv_log_file, TEST_LOG_ALWAYS,
1265         ("main(0x%p): waiting for execution interval (%d seconds)\n",
1266         PR_GetCurrentThread(), execution));
1267 
1268     WaitForCompletion(execution);
1269 
1270     TimeOfDayMessage("Shutting down", PR_GetCurrentThread());
1271 
1272     if (clients != 0)
1273     {
1274         for (index = 0; index < clients; ++index)
1275         {
1276             TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
1277                 ("main(0x%p): notifying client(0x%p) to stop\n",
1278                 PR_GetCurrentThread(), client[index].thread));
1279 
1280             PR_Lock(client[index].ml);
1281             if (cs_run == client[index].state)
1282             {
1283                 client[index].state = cs_stop;
1284                 PR_Interrupt(client[index].thread);
1285                 while (cs_stop == client[index].state)
1286                     PR_WaitCondVar(
1287                         client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
1288             }
1289             PR_Unlock(client[index].ml);
1290 
1291             TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
1292                 ("main(0x%p): joining client(0x%p)\n",
1293                 PR_GetCurrentThread(), client[index].thread));
1294 
1295 		    joinStatus = JoinThread(client[index].thread);
1296 		    TEST_ASSERT(PR_SUCCESS == joinStatus);
1297             PR_DestroyCondVar(client[index].stateChange);
1298             PR_DestroyLock(client[index].ml);
1299         }
1300         PR_DELETE(client);
1301     }
1302 
1303     if (NULL != server)
1304     {
1305         /* All clients joined - retrieve the server */
1306         TEST_LOG(
1307             cltsrv_log_file, TEST_LOG_NOTICE,
1308             ("main(0x%p): notifying server(0x%p) to stop\n",
1309             PR_GetCurrentThread(), server->thread));
1310 
1311         PR_Lock(server->ml);
1312         server->state = cs_stop;
1313         PR_Interrupt(server->thread);
1314         while (cs_exit != server->state)
1315             PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
1316         PR_Unlock(server->ml);
1317 
1318         TEST_LOG(
1319             cltsrv_log_file, TEST_LOG_NOTICE,
1320             ("main(0x%p): joining server(0x%p)\n",
1321             PR_GetCurrentThread(), server->thread));
1322         joinStatus = JoinThread(server->thread);
1323         TEST_ASSERT(PR_SUCCESS == joinStatus);
1324 
1325         PR_DestroyCondVar(server->stateChange);
1326         PR_DestroyCondVar(server->pool.exiting);
1327         PR_DestroyCondVar(server->pool.acceptComplete);
1328         PR_DestroyLock(server->ml);
1329         PR_DELETE(server);
1330     }
1331 
1332     TEST_LOG(
1333         cltsrv_log_file, TEST_LOG_ALWAYS,
1334         ("main(0x%p): test complete\n", PR_GetCurrentThread()));
1335 
1336 	if (thread_provider == thread_win32)
1337 		thread_type = "\nWin32 Thread Statistics\n";
1338 	else if (thread_provider == thread_pthread)
1339 		thread_type = "\npthread Statistics\n";
1340 	else if (thread_provider == thread_sproc)
1341 		thread_type = "\nsproc Statistics\n";
1342     else {
1343 		PR_ASSERT(thread_provider == thread_nspr);
1344 		thread_type = "\nPRThread Statistics\nn";
1345 	}
1346 
1347     PT_FPrintStats(debug_out, thread_type);
1348 
1349     TimeOfDayMessage("Test exiting at", PR_GetCurrentThread());
1350     PR_Cleanup();
1351     return 0;
1352 }  /* main */
1353 
1354 /* cltsrv.c */
1355