1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/tests/thrpool_server.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,572 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/*********************************************************************** 1.10 +** 1.11 +** Name: thrpool.c 1.12 +** 1.13 +** Description: Test threadpool functionality. 1.14 +** 1.15 +** Modification History: 1.16 +*/ 1.17 +#include "primpl.h" 1.18 + 1.19 +#include "plgetopt.h" 1.20 + 1.21 +#include <stdio.h> 1.22 +#include <string.h> 1.23 +#include <errno.h> 1.24 +#ifdef XP_UNIX 1.25 +#include <sys/mman.h> 1.26 +#endif 1.27 +#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) 1.28 +#include <pthread.h> 1.29 +#endif 1.30 + 1.31 +/* for getcwd */ 1.32 +#if defined(XP_UNIX) || defined (XP_OS2) || defined(XP_BEOS) 1.33 +#include <unistd.h> 1.34 +#elif defined(XP_PC) 1.35 +#include <direct.h> 1.36 +#endif 1.37 + 1.38 +#ifdef WIN32 1.39 +#include <process.h> 1.40 +#endif 1.41 + 1.42 +static int _debug_on = 0; 1.43 +static char *program_name = NULL; 1.44 +static void serve_client_write(void *arg); 1.45 + 1.46 +#include "obsolete/prsem.h" 1.47 + 1.48 +#ifdef XP_PC 1.49 +#define mode_t int 1.50 +#endif 1.51 + 1.52 +#define DPRINTF(arg) if (_debug_on) printf arg 1.53 + 1.54 + 1.55 +#define BUF_DATA_SIZE (2 * 1024) 1.56 +#define TCP_MESG_SIZE 1024 1.57 +#define NUM_TCP_CLIENTS 10 /* for a listen queue depth of 5 */ 1.58 + 1.59 + 1.60 +#define NUM_TCP_CONNECTIONS_PER_CLIENT 10 1.61 +#define NUM_TCP_MESGS_PER_CONNECTION 10 1.62 +#define TCP_SERVER_PORT 10000 1.63 +#define SERVER_MAX_BIND_COUNT 100 1.64 + 1.65 +#ifdef WINCE 1.66 +char *getcwd(char *buf, size_t size) 1.67 +{ 1.68 + wchar_t wpath[MAX_PATH]; 1.69 + _wgetcwd(wpath, MAX_PATH); 1.70 + WideCharToMultiByte(CP_ACP, 0, wpath, -1, buf, size, 0, 0); 1.71 +} 1.72 + 1.73 +#define perror(s) 1.74 +#endif 1.75 + 1.76 +static PRInt32 num_tcp_clients = NUM_TCP_CLIENTS; 1.77 +static PRInt32 num_tcp_connections_per_client = NUM_TCP_CONNECTIONS_PER_CLIENT; 1.78 +static PRInt32 tcp_mesg_size = TCP_MESG_SIZE; 1.79 +static PRInt32 num_tcp_mesgs_per_connection = NUM_TCP_MESGS_PER_CONNECTION; 1.80 +static void TCP_Server_Accept(void *arg); 1.81 + 1.82 + 1.83 +int failed_already=0; 1.84 +typedef struct buffer { 1.85 + char data[BUF_DATA_SIZE]; 1.86 +} buffer; 1.87 + 1.88 + 1.89 +typedef struct Server_Param { 1.90 + PRJobIoDesc iod; /* socket to read from/write to */ 1.91 + PRInt32 datalen; /* bytes of data transfered in each read/write */ 1.92 + PRNetAddr netaddr; 1.93 + PRMonitor *exit_mon; /* monitor to signal on exit */ 1.94 + PRInt32 *job_counterp; /* counter to decrement, before exit */ 1.95 + PRInt32 conn_counter; /* counter to decrement, before exit */ 1.96 + PRThreadPool *tp; 1.97 +} Server_Param; 1.98 + 1.99 +typedef struct Serve_Client_Param { 1.100 + PRJobIoDesc iod; /* socket to read from/write to */ 1.101 + PRInt32 datalen; /* bytes of data transfered in each read/write */ 1.102 + PRMonitor *exit_mon; /* monitor to signal on exit */ 1.103 + PRInt32 *job_counterp; /* counter to decrement, before exit */ 1.104 + PRThreadPool *tp; 1.105 +} Serve_Client_Param; 1.106 + 1.107 +typedef struct Session { 1.108 + PRJobIoDesc iod; /* socket to read from/write to */ 1.109 + buffer *in_buf; 1.110 + PRInt32 bytes; 1.111 + PRInt32 msg_num; 1.112 + PRInt32 bytes_read; 1.113 + PRMonitor *exit_mon; /* monitor to signal on exit */ 1.114 + PRInt32 *job_counterp; /* counter to decrement, before exit */ 1.115 + PRThreadPool *tp; 1.116 +} Session; 1.117 + 1.118 +static void 1.119 +serve_client_read(void *arg) 1.120 +{ 1.121 + Session *sp = (Session *) arg; 1.122 + int rem; 1.123 + int bytes; 1.124 + int offset; 1.125 + PRFileDesc *sockfd; 1.126 + char *buf; 1.127 + PRJob *jobp; 1.128 + 1.129 + PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT; 1.130 + 1.131 + sockfd = sp->iod.socket; 1.132 + buf = sp->in_buf->data; 1.133 + 1.134 + PR_ASSERT(sp->msg_num < num_tcp_mesgs_per_connection); 1.135 + PR_ASSERT(sp->bytes_read < sp->bytes); 1.136 + 1.137 + offset = sp->bytes_read; 1.138 + rem = sp->bytes - offset; 1.139 + bytes = PR_Recv(sockfd, buf + offset, rem, 0, timeout); 1.140 + if (bytes < 0) { 1.141 + return; 1.142 + } 1.143 + sp->bytes_read += bytes; 1.144 + sp->iod.timeout = PR_SecondsToInterval(60); 1.145 + if (sp->bytes_read < sp->bytes) { 1.146 + jobp = PR_QueueJob_Read(sp->tp, &sp->iod, serve_client_read, sp, 1.147 + PR_FALSE); 1.148 + PR_ASSERT(NULL != jobp); 1.149 + return; 1.150 + } 1.151 + PR_ASSERT(sp->bytes_read == sp->bytes); 1.152 + DPRINTF(("serve_client: read complete, msg(%d) \n", sp->msg_num)); 1.153 + 1.154 + sp->iod.timeout = PR_SecondsToInterval(60); 1.155 + jobp = PR_QueueJob_Write(sp->tp, &sp->iod, serve_client_write, sp, 1.156 + PR_FALSE); 1.157 + PR_ASSERT(NULL != jobp); 1.158 + 1.159 + return; 1.160 +} 1.161 + 1.162 +static void 1.163 +serve_client_write(void *arg) 1.164 +{ 1.165 + Session *sp = (Session *) arg; 1.166 + int bytes; 1.167 + PRFileDesc *sockfd; 1.168 + char *buf; 1.169 + PRJob *jobp; 1.170 + 1.171 + sockfd = sp->iod.socket; 1.172 + buf = sp->in_buf->data; 1.173 + 1.174 + PR_ASSERT(sp->msg_num < num_tcp_mesgs_per_connection); 1.175 + 1.176 + bytes = PR_Send(sockfd, buf, sp->bytes, 0, PR_INTERVAL_NO_TIMEOUT); 1.177 + PR_ASSERT(bytes == sp->bytes); 1.178 + 1.179 + if (bytes < 0) { 1.180 + return; 1.181 + } 1.182 + DPRINTF(("serve_client: write complete, msg(%d) \n", sp->msg_num)); 1.183 + sp->msg_num++; 1.184 + if (sp->msg_num < num_tcp_mesgs_per_connection) { 1.185 + sp->bytes_read = 0; 1.186 + sp->iod.timeout = PR_SecondsToInterval(60); 1.187 + jobp = PR_QueueJob_Read(sp->tp, &sp->iod, serve_client_read, sp, 1.188 + PR_FALSE); 1.189 + PR_ASSERT(NULL != jobp); 1.190 + return; 1.191 + } 1.192 + 1.193 + DPRINTF(("serve_client: read/write complete, msg(%d) \n", sp->msg_num)); 1.194 + if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) { 1.195 + fprintf(stderr,"%s: ERROR - PR_Shutdown\n", program_name); 1.196 + } 1.197 + 1.198 + PR_Close(sockfd); 1.199 + PR_EnterMonitor(sp->exit_mon); 1.200 + --(*sp->job_counterp); 1.201 + PR_Notify(sp->exit_mon); 1.202 + PR_ExitMonitor(sp->exit_mon); 1.203 + 1.204 + PR_DELETE(sp->in_buf); 1.205 + PR_DELETE(sp); 1.206 + 1.207 + return; 1.208 +} 1.209 + 1.210 +/* 1.211 + * Serve_Client 1.212 + * Thread, started by the server, for serving a client connection. 1.213 + * Reads data from socket and writes it back, unmodified, and 1.214 + * closes the socket 1.215 + */ 1.216 +static void PR_CALLBACK 1.217 +Serve_Client(void *arg) 1.218 +{ 1.219 + Serve_Client_Param *scp = (Serve_Client_Param *) arg; 1.220 + buffer *in_buf; 1.221 + Session *sp; 1.222 + PRJob *jobp; 1.223 + 1.224 + sp = PR_NEW(Session); 1.225 + sp->iod = scp->iod; 1.226 + 1.227 + in_buf = PR_NEW(buffer); 1.228 + if (in_buf == NULL) { 1.229 + fprintf(stderr,"%s: failed to alloc buffer struct\n",program_name); 1.230 + failed_already=1; 1.231 + return; 1.232 + } 1.233 + 1.234 + sp->in_buf = in_buf; 1.235 + sp->bytes = scp->datalen; 1.236 + sp->msg_num = 0; 1.237 + sp->bytes_read = 0; 1.238 + sp->tp = scp->tp; 1.239 + sp->exit_mon = scp->exit_mon; 1.240 + sp->job_counterp = scp->job_counterp; 1.241 + 1.242 + sp->iod.timeout = PR_SecondsToInterval(60); 1.243 + jobp = PR_QueueJob_Read(sp->tp, &sp->iod, serve_client_read, sp, 1.244 + PR_FALSE); 1.245 + PR_ASSERT(NULL != jobp); 1.246 + PR_DELETE(scp); 1.247 +} 1.248 + 1.249 +static void 1.250 +print_stats(void *arg) 1.251 +{ 1.252 + Server_Param *sp = (Server_Param *) arg; 1.253 + PRThreadPool *tp = sp->tp; 1.254 + PRInt32 counter; 1.255 + PRJob *jobp; 1.256 + 1.257 + PR_EnterMonitor(sp->exit_mon); 1.258 + counter = (*sp->job_counterp); 1.259 + PR_ExitMonitor(sp->exit_mon); 1.260 + 1.261 + printf("PRINT_STATS: #client connections = %d\n",counter); 1.262 + 1.263 + 1.264 + jobp = PR_QueueJob_Timer(tp, PR_MillisecondsToInterval(500), 1.265 + print_stats, sp, PR_FALSE); 1.266 + 1.267 + PR_ASSERT(NULL != jobp); 1.268 +} 1.269 + 1.270 +static int job_counter = 0; 1.271 +/* 1.272 + * TCP Server 1.273 + * Server binds an address to a socket, starts a client process and 1.274 + * listens for incoming connections. 1.275 + * Each client connects to the server and sends a chunk of data 1.276 + * Starts a Serve_Client job for each incoming connection, to read 1.277 + * the data from the client and send it back to the client, unmodified. 1.278 + * Each client checks that data received from server is same as the 1.279 + * data it sent to the server. 1.280 + * Finally, the threadpool is shutdown 1.281 + */ 1.282 +static void PR_CALLBACK 1.283 +TCP_Server(void *arg) 1.284 +{ 1.285 + PRThreadPool *tp = (PRThreadPool *) arg; 1.286 + Server_Param *sp; 1.287 + PRFileDesc *sockfd; 1.288 + PRNetAddr netaddr; 1.289 + PRMonitor *sc_mon; 1.290 + PRJob *jobp; 1.291 + int i; 1.292 + PRStatus rval; 1.293 + 1.294 + /* 1.295 + * Create a tcp socket 1.296 + */ 1.297 + if ((sockfd = PR_NewTCPSocket()) == NULL) { 1.298 + fprintf(stderr,"%s: PR_NewTCPSocket failed\n", program_name); 1.299 + return; 1.300 + } 1.301 + memset(&netaddr, 0 , sizeof(netaddr)); 1.302 + netaddr.inet.family = PR_AF_INET; 1.303 + netaddr.inet.port = PR_htons(TCP_SERVER_PORT); 1.304 + netaddr.inet.ip = PR_htonl(PR_INADDR_ANY); 1.305 + /* 1.306 + * try a few times to bind server's address, if addresses are in 1.307 + * use 1.308 + */ 1.309 + i = 0; 1.310 + while (PR_Bind(sockfd, &netaddr) < 0) { 1.311 + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { 1.312 + netaddr.inet.port += 2; 1.313 + if (i++ < SERVER_MAX_BIND_COUNT) 1.314 + continue; 1.315 + } 1.316 + fprintf(stderr,"%s: ERROR - PR_Bind failed\n", program_name); 1.317 + perror("PR_Bind"); 1.318 + failed_already=1; 1.319 + return; 1.320 + } 1.321 + 1.322 + if (PR_Listen(sockfd, 32) < 0) { 1.323 + fprintf(stderr,"%s: ERROR - PR_Listen failed\n", program_name); 1.324 + failed_already=1; 1.325 + return; 1.326 + } 1.327 + 1.328 + if (PR_GetSockName(sockfd, &netaddr) < 0) { 1.329 + fprintf(stderr,"%s: ERROR - PR_GetSockName failed\n", program_name); 1.330 + failed_already=1; 1.331 + return; 1.332 + } 1.333 + 1.334 + DPRINTF(( 1.335 + "TCP_Server: PR_BIND netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n", 1.336 + netaddr.inet.ip, netaddr.inet.port)); 1.337 + 1.338 + sp = PR_NEW(Server_Param); 1.339 + if (sp == NULL) { 1.340 + fprintf(stderr,"%s: PR_NEW failed\n", program_name); 1.341 + failed_already=1; 1.342 + return; 1.343 + } 1.344 + sp->iod.socket = sockfd; 1.345 + sp->iod.timeout = PR_SecondsToInterval(60); 1.346 + sp->datalen = tcp_mesg_size; 1.347 + sp->exit_mon = sc_mon; 1.348 + sp->job_counterp = &job_counter; 1.349 + sp->conn_counter = 0; 1.350 + sp->tp = tp; 1.351 + sp->netaddr = netaddr; 1.352 + 1.353 + /* create and cancel an io job */ 1.354 + jobp = PR_QueueJob_Accept(tp, &sp->iod, TCP_Server_Accept, sp, 1.355 + PR_FALSE); 1.356 + PR_ASSERT(NULL != jobp); 1.357 + rval = PR_CancelJob(jobp); 1.358 + PR_ASSERT(PR_SUCCESS == rval); 1.359 + 1.360 + /* 1.361 + * create the client process 1.362 + */ 1.363 + { 1.364 +#define MAX_ARGS 4 1.365 + char *argv[MAX_ARGS + 1]; 1.366 + int index = 0; 1.367 + char port[32]; 1.368 + char path[1024 + sizeof("/thrpool_client")]; 1.369 + 1.370 + getcwd(path, sizeof(path)); 1.371 + 1.372 + (void)strcat(path, "/thrpool_client"); 1.373 +#ifdef XP_PC 1.374 + (void)strcat(path, ".exe"); 1.375 +#endif 1.376 + argv[index++] = path; 1.377 + sprintf(port,"%d",PR_ntohs(netaddr.inet.port)); 1.378 + if (_debug_on) 1.379 + { 1.380 + argv[index++] = "-d"; 1.381 + argv[index++] = "-p"; 1.382 + argv[index++] = port; 1.383 + argv[index++] = NULL; 1.384 + } else { 1.385 + argv[index++] = "-p"; 1.386 + argv[index++] = port; 1.387 + argv[index++] = NULL; 1.388 + } 1.389 + PR_ASSERT(MAX_ARGS >= (index - 1)); 1.390 + 1.391 + DPRINTF(("creating client process %s ...\n", path)); 1.392 + if (PR_FAILURE == PR_CreateProcessDetached(path, argv, NULL, NULL)) { 1.393 + fprintf(stderr, 1.394 + "thrpool_server: ERROR - PR_CreateProcessDetached failed\n"); 1.395 + failed_already=1; 1.396 + return; 1.397 + } 1.398 + } 1.399 + 1.400 + sc_mon = PR_NewMonitor(); 1.401 + if (sc_mon == NULL) { 1.402 + fprintf(stderr,"%s: PR_NewMonitor failed\n", program_name); 1.403 + failed_already=1; 1.404 + return; 1.405 + } 1.406 + 1.407 + sp->iod.socket = sockfd; 1.408 + sp->iod.timeout = PR_SecondsToInterval(60); 1.409 + sp->datalen = tcp_mesg_size; 1.410 + sp->exit_mon = sc_mon; 1.411 + sp->job_counterp = &job_counter; 1.412 + sp->conn_counter = 0; 1.413 + sp->tp = tp; 1.414 + sp->netaddr = netaddr; 1.415 + 1.416 + /* create and cancel a timer job */ 1.417 + jobp = PR_QueueJob_Timer(tp, PR_MillisecondsToInterval(5000), 1.418 + print_stats, sp, PR_FALSE); 1.419 + PR_ASSERT(NULL != jobp); 1.420 + rval = PR_CancelJob(jobp); 1.421 + PR_ASSERT(PR_SUCCESS == rval); 1.422 + 1.423 + DPRINTF(("TCP_Server: Accepting connections \n")); 1.424 + 1.425 + jobp = PR_QueueJob_Accept(tp, &sp->iod, TCP_Server_Accept, sp, 1.426 + PR_FALSE); 1.427 + PR_ASSERT(NULL != jobp); 1.428 + return; 1.429 +} 1.430 + 1.431 +static void 1.432 +TCP_Server_Accept(void *arg) 1.433 +{ 1.434 + Server_Param *sp = (Server_Param *) arg; 1.435 + PRThreadPool *tp = sp->tp; 1.436 + Serve_Client_Param *scp; 1.437 + PRFileDesc *newsockfd; 1.438 + PRJob *jobp; 1.439 + 1.440 + if ((newsockfd = PR_Accept(sp->iod.socket, &sp->netaddr, 1.441 + PR_INTERVAL_NO_TIMEOUT)) == NULL) { 1.442 + fprintf(stderr,"%s: ERROR - PR_Accept failed\n", program_name); 1.443 + failed_already=1; 1.444 + goto exit; 1.445 + } 1.446 + scp = PR_NEW(Serve_Client_Param); 1.447 + if (scp == NULL) { 1.448 + fprintf(stderr,"%s: PR_NEW failed\n", program_name); 1.449 + failed_already=1; 1.450 + goto exit; 1.451 + } 1.452 + 1.453 + /* 1.454 + * Start a Serve_Client job for each incoming connection 1.455 + */ 1.456 + scp->iod.socket = newsockfd; 1.457 + scp->iod.timeout = PR_SecondsToInterval(60); 1.458 + scp->datalen = tcp_mesg_size; 1.459 + scp->exit_mon = sp->exit_mon; 1.460 + scp->job_counterp = sp->job_counterp; 1.461 + scp->tp = sp->tp; 1.462 + 1.463 + PR_EnterMonitor(sp->exit_mon); 1.464 + (*sp->job_counterp)++; 1.465 + PR_ExitMonitor(sp->exit_mon); 1.466 + jobp = PR_QueueJob(tp, Serve_Client, scp, 1.467 + PR_FALSE); 1.468 + 1.469 + PR_ASSERT(NULL != jobp); 1.470 + DPRINTF(("TCP_Server: Created Serve_Client = 0x%lx\n", jobp)); 1.471 + 1.472 + /* 1.473 + * single-threaded update; no lock needed 1.474 + */ 1.475 + sp->conn_counter++; 1.476 + if (sp->conn_counter < 1.477 + (num_tcp_clients * num_tcp_connections_per_client)) { 1.478 + jobp = PR_QueueJob_Accept(tp, &sp->iod, TCP_Server_Accept, sp, 1.479 + PR_FALSE); 1.480 + PR_ASSERT(NULL != jobp); 1.481 + return; 1.482 + } 1.483 + jobp = PR_QueueJob_Timer(tp, PR_MillisecondsToInterval(500), 1.484 + print_stats, sp, PR_FALSE); 1.485 + 1.486 + PR_ASSERT(NULL != jobp); 1.487 + DPRINTF(("TCP_Server: Created print_stats timer job = 0x%lx\n", jobp)); 1.488 + 1.489 +exit: 1.490 + PR_EnterMonitor(sp->exit_mon); 1.491 + /* Wait for server jobs to finish */ 1.492 + while (0 != *sp->job_counterp) { 1.493 + PR_Wait(sp->exit_mon, PR_INTERVAL_NO_TIMEOUT); 1.494 + DPRINTF(("TCP_Server: conn_counter = %d\n", 1.495 + *sp->job_counterp)); 1.496 + } 1.497 + 1.498 + PR_ExitMonitor(sp->exit_mon); 1.499 + if (sp->iod.socket) { 1.500 + PR_Close(sp->iod.socket); 1.501 + } 1.502 + PR_DestroyMonitor(sp->exit_mon); 1.503 + printf("%30s","TCP_Socket_Client_Server_Test:"); 1.504 + printf("%2ld Server %2ld Clients %2ld connections_per_client\n",1l, 1.505 + num_tcp_clients, num_tcp_connections_per_client); 1.506 + printf("%30s %2ld messages_per_connection %4ld bytes_per_message\n",":", 1.507 + num_tcp_mesgs_per_connection, tcp_mesg_size); 1.508 + 1.509 + DPRINTF(("%s: calling PR_ShutdownThreadPool\n", program_name)); 1.510 + PR_ShutdownThreadPool(sp->tp); 1.511 + PR_DELETE(sp); 1.512 +} 1.513 + 1.514 +/************************************************************************/ 1.515 + 1.516 +#define DEFAULT_INITIAL_THREADS 4 1.517 +#define DEFAULT_MAX_THREADS 100 1.518 +#define DEFAULT_STACKSIZE (512 * 1024) 1.519 + 1.520 +int main(int argc, char **argv) 1.521 +{ 1.522 + PRInt32 initial_threads = DEFAULT_INITIAL_THREADS; 1.523 + PRInt32 max_threads = DEFAULT_MAX_THREADS; 1.524 + PRInt32 stacksize = DEFAULT_STACKSIZE; 1.525 + PRThreadPool *tp = NULL; 1.526 + PRStatus rv; 1.527 + PRJob *jobp; 1.528 + 1.529 + /* 1.530 + * -d debug mode 1.531 + */ 1.532 + PLOptStatus os; 1.533 + PLOptState *opt; 1.534 + 1.535 + program_name = argv[0]; 1.536 + opt = PL_CreateOptState(argc, argv, "d"); 1.537 + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 1.538 + { 1.539 + if (PL_OPT_BAD == os) continue; 1.540 + switch (opt->option) 1.541 + { 1.542 + case 'd': /* debug mode */ 1.543 + _debug_on = 1; 1.544 + break; 1.545 + default: 1.546 + break; 1.547 + } 1.548 + } 1.549 + PL_DestroyOptState(opt); 1.550 + 1.551 + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 1.552 + PR_STDIO_INIT(); 1.553 + 1.554 + PR_SetConcurrency(4); 1.555 + 1.556 + tp = PR_CreateThreadPool(initial_threads, max_threads, stacksize); 1.557 + if (NULL == tp) { 1.558 + printf("PR_CreateThreadPool failed\n"); 1.559 + failed_already=1; 1.560 + goto done; 1.561 + } 1.562 + jobp = PR_QueueJob(tp, TCP_Server, tp, PR_TRUE); 1.563 + rv = PR_JoinJob(jobp); 1.564 + PR_ASSERT(PR_SUCCESS == rv); 1.565 + 1.566 + DPRINTF(("%s: calling PR_JoinThreadPool\n", program_name)); 1.567 + rv = PR_JoinThreadPool(tp); 1.568 + PR_ASSERT(PR_SUCCESS == rv); 1.569 + DPRINTF(("%s: returning from PR_JoinThreadPool\n", program_name)); 1.570 + 1.571 +done: 1.572 + PR_Cleanup(); 1.573 + if (failed_already) return 1; 1.574 + else return 0; 1.575 +}