|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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> |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
1042 static Verbosity IncrementVerbosity(void) |
|
1043 { |
|
1044 PRIntn verboge = (PRIntn)verbosity + 1; |
|
1045 return (Verbosity)verboge; |
|
1046 } /* IncrementVerbosity */ |
|
1047 |
|
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 */ |