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