michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * michael@0: * Notes: michael@0: * [1] lth. The call to Sleep() is a hack to get the test case to run michael@0: * on Windows 95. Without it, the test case fails with an error michael@0: * WSAECONNRESET following a recv() call. The error is caused by the michael@0: * server side thread termination without a shutdown() or closesocket() michael@0: * call. Windows docmunentation suggests that this is predicted michael@0: * behavior; that other platforms get away with it is ... serindipity. michael@0: * The test case should shutdown() or closesocket() before michael@0: * thread termination. I didn't have time to figure out where or how michael@0: * to do it. The Sleep() call inserts enough delay to allow the michael@0: * client side to recv() all his data before the server side thread michael@0: * terminates. Whew! ... michael@0: * michael@0: ** Modification History: michael@0: * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. michael@0: * The debug mode will print all of the printfs associated with this test. michael@0: * The regress mode will be the default mode. Since the regress tool limits michael@0: * the output to a one line status:PASS or FAIL,all of the printf statements michael@0: * have been handled with an if (debug_mode) statement. michael@0: */ michael@0: michael@0: #include "prclist.h" michael@0: #include "prcvar.h" michael@0: #include "prerror.h" michael@0: #include "prinit.h" michael@0: #include "prinrval.h" michael@0: #include "prio.h" michael@0: #include "prlock.h" michael@0: #include "prlog.h" michael@0: #include "prtime.h" michael@0: #include "prmem.h" michael@0: #include "prnetdb.h" michael@0: #include "prprf.h" michael@0: #include "prthread.h" michael@0: michael@0: #include "pprio.h" michael@0: #include "primpl.h" michael@0: michael@0: #include "plstr.h" michael@0: #include "plerror.h" michael@0: #include "plgetopt.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #if defined(XP_UNIX) michael@0: #include michael@0: #endif michael@0: michael@0: /* michael@0: ** This is the beginning of the test michael@0: */ michael@0: michael@0: #define RECV_FLAGS 0 michael@0: #define SEND_FLAGS 0 michael@0: #define DEFAULT_LOW 0 michael@0: #define DEFAULT_HIGH 0 michael@0: #define BUFFER_SIZE 1024 michael@0: #define DEFAULT_BACKLOG 5 michael@0: #define DEFAULT_PORT 12849 michael@0: #define DEFAULT_CLIENTS 1 michael@0: #define ALLOWED_IN_ACCEPT 1 michael@0: #define DEFAULT_CLIPPING 1000 michael@0: #define DEFAULT_WORKERS_MIN 1 michael@0: #define DEFAULT_WORKERS_MAX 1 michael@0: #define DEFAULT_SERVER "localhost" michael@0: #define DEFAULT_EXECUTION_TIME 10 michael@0: #define DEFAULT_CLIENT_TIMEOUT 4000 michael@0: #define DEFAULT_SERVER_TIMEOUT 4000 michael@0: #define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH michael@0: michael@0: typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t; michael@0: michael@0: static void PR_CALLBACK Worker(void *arg); michael@0: typedef struct CSPool_s CSPool_t; michael@0: typedef struct CSWorker_s CSWorker_t; michael@0: typedef struct CSServer_s CSServer_t; michael@0: typedef enum Verbosity michael@0: { michael@0: TEST_LOG_ALWAYS, michael@0: TEST_LOG_ERROR, michael@0: TEST_LOG_WARNING, michael@0: TEST_LOG_NOTICE, michael@0: TEST_LOG_INFO, michael@0: TEST_LOG_STATUS, michael@0: TEST_LOG_VERBOSE michael@0: } Verbosity; michael@0: michael@0: static PRInt32 domain = AF_INET; michael@0: static PRInt32 protocol = 6; /* TCP */ michael@0: static PRFileDesc *debug_out = NULL; michael@0: static PRBool debug_mode = PR_FALSE; michael@0: static PRBool pthread_stats = PR_FALSE; michael@0: static Verbosity verbosity = TEST_LOG_ALWAYS; michael@0: static PRThreadScope thread_scope = PR_LOCAL_THREAD; michael@0: michael@0: struct CSWorker_s michael@0: { michael@0: PRCList element; /* list of the server's workers */ michael@0: michael@0: PRThread *thread; /* this worker objects thread */ michael@0: CSServer_t *server; /* back pointer to server structure */ michael@0: }; michael@0: michael@0: struct CSPool_s michael@0: { michael@0: PRCondVar *exiting; michael@0: PRCondVar *acceptComplete; michael@0: PRUint32 accepting, active, workers; michael@0: }; michael@0: michael@0: struct CSServer_s michael@0: { michael@0: PRCList list; /* head of worker list */ michael@0: michael@0: PRLock *ml; michael@0: PRThread *thread; /* the main server thread */ michael@0: PRCondVar *stateChange; michael@0: michael@0: PRUint16 port; /* port we're listening on */ michael@0: PRUint32 backlog; /* size of our listener backlog */ michael@0: PRFileDesc *listener; /* the fd accepting connections */ michael@0: michael@0: CSPool_t pool; /* statistics on worker threads */ michael@0: CSState_t state; /* the server's state */ michael@0: struct /* controlling worker counts */ michael@0: { michael@0: PRUint32 minimum, maximum, accepting; michael@0: } workers; michael@0: michael@0: /* statistics */ michael@0: PRIntervalTime started, stopped; michael@0: PRUint32 operations, bytesTransferred; michael@0: }; michael@0: michael@0: typedef struct CSDescriptor_s michael@0: { michael@0: PRInt32 size; /* size of transfer */ michael@0: char filename[60]; /* filename, null padded */ michael@0: } CSDescriptor_t; michael@0: michael@0: typedef struct CSClient_s michael@0: { michael@0: PRLock *ml; michael@0: PRThread *thread; michael@0: PRCondVar *stateChange; michael@0: PRNetAddr serverAddress; michael@0: michael@0: CSState_t state; michael@0: michael@0: /* statistics */ michael@0: PRIntervalTime started, stopped; michael@0: PRUint32 operations, bytesTransferred; michael@0: } CSClient_t; michael@0: michael@0: #define TEST_LOG(l, p, a) \ michael@0: do { \ michael@0: if (debug_mode || (p <= verbosity)) printf a; \ michael@0: } while (0) michael@0: michael@0: PRLogModuleInfo *cltsrv_log_file = NULL; michael@0: michael@0: #define MY_ASSERT(_expr) \ michael@0: ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) michael@0: michael@0: #define TEST_ASSERT(_expr) \ michael@0: ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) michael@0: michael@0: static void _MY_Assert(const char *s, const char *file, PRIntn ln) michael@0: { michael@0: PL_PrintError(NULL); michael@0: PR_Assert(s, file, ln); michael@0: } /* _MY_Assert */ michael@0: michael@0: static PRBool Aborted(PRStatus rv) michael@0: { michael@0: return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ? michael@0: PR_TRUE : PR_FALSE; michael@0: } michael@0: michael@0: static void TimeOfDayMessage(const char *msg, PRThread* me) michael@0: { michael@0: char buffer[100]; michael@0: PRExplodedTime tod; michael@0: PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod); michael@0: (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ALWAYS, michael@0: ("%s(0x%p): %s\n", msg, me, buffer)); michael@0: } /* TimeOfDayMessage */ michael@0: michael@0: michael@0: static void PR_CALLBACK Client(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: PRIntn index; michael@0: char buffer[1024]; michael@0: PRFileDesc *fd = NULL; michael@0: PRUintn clipping = DEFAULT_CLIPPING; michael@0: PRThread *me = PR_GetCurrentThread(); michael@0: CSClient_t *client = (CSClient_t*)arg; michael@0: CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); michael@0: PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT); michael@0: michael@0: michael@0: for (index = 0; index < sizeof(buffer); ++index) michael@0: buffer[index] = (char)index; michael@0: michael@0: client->started = PR_IntervalNow(); michael@0: michael@0: PR_Lock(client->ml); michael@0: client->state = cs_run; michael@0: PR_NotifyCondVar(client->stateChange); michael@0: PR_Unlock(client->ml); michael@0: michael@0: TimeOfDayMessage("Client started at", me); michael@0: michael@0: while (cs_run == client->state) michael@0: { michael@0: PRInt32 bytes, descbytes, filebytes, netbytes; michael@0: michael@0: (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer)); michael@0: TEST_LOG(cltsrv_log_file, TEST_LOG_INFO, michael@0: ("\tClient(0x%p): connecting to server at %s\n", me, buffer)); michael@0: michael@0: fd = PR_Socket(domain, SOCK_STREAM, protocol); michael@0: TEST_ASSERT(NULL != fd); michael@0: rv = PR_Connect(fd, &client->serverAddress, timeout); michael@0: if (PR_FAILURE == rv) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\tClient(0x%p): conection failed (%d, %d)\n", michael@0: me, PR_GetError(), PR_GetOSError())); michael@0: goto aborted; michael@0: } michael@0: michael@0: memset(descriptor, 0, sizeof(*descriptor)); michael@0: descriptor->size = PR_htonl(descbytes = rand() % clipping); michael@0: PR_snprintf( michael@0: descriptor->filename, sizeof(descriptor->filename), michael@0: "CS%p%p-%p.dat", client->started, me, client->operations); michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes)); michael@0: bytes = PR_Send( michael@0: fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout); michael@0: if (sizeof(CSDescriptor_t) != bytes) michael@0: { michael@0: if (Aborted(PR_FAILURE)) goto aborted; michael@0: if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\tClient(0x%p): send descriptor timeout\n", me)); michael@0: goto retry; michael@0: } michael@0: } michael@0: TEST_ASSERT(sizeof(*descriptor) == bytes); michael@0: michael@0: netbytes = 0; michael@0: while (netbytes < descbytes) michael@0: { michael@0: filebytes = sizeof(buffer); michael@0: if ((descbytes - netbytes) < filebytes) michael@0: filebytes = descbytes - netbytes; michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\tClient(0x%p): sending %d bytes\n", me, filebytes)); michael@0: bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout); michael@0: if (filebytes != bytes) michael@0: { michael@0: if (Aborted(PR_FAILURE)) goto aborted; michael@0: if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\tClient(0x%p): send data timeout\n", me)); michael@0: goto retry; michael@0: } michael@0: } michael@0: TEST_ASSERT(bytes == filebytes); michael@0: netbytes += bytes; michael@0: } michael@0: filebytes = 0; michael@0: while (filebytes < descbytes) michael@0: { michael@0: netbytes = sizeof(buffer); michael@0: if ((descbytes - filebytes) < netbytes) michael@0: netbytes = descbytes - filebytes; michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\tClient(0x%p): receiving %d bytes\n", me, netbytes)); michael@0: bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); michael@0: if (-1 == bytes) michael@0: { michael@0: if (Aborted(PR_FAILURE)) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\tClient(0x%p): receive data aborted\n", me)); michael@0: goto aborted; michael@0: } michael@0: else if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\tClient(0x%p): receive data timeout\n", me)); michael@0: else michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\tClient(0x%p): receive error (%d, %d)\n", michael@0: me, PR_GetError(), PR_GetOSError())); michael@0: goto retry; michael@0: } michael@0: if (0 == bytes) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\t\tClient(0x%p): unexpected end of stream\n", michael@0: PR_GetCurrentThread())); michael@0: break; michael@0: } michael@0: filebytes += bytes; michael@0: } michael@0: michael@0: rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH); michael@0: if (Aborted(rv)) goto aborted; michael@0: TEST_ASSERT(PR_SUCCESS == rv); michael@0: retry: michael@0: (void)PR_Close(fd); fd = NULL; michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_INFO, michael@0: ("\tClient(0x%p): disconnected from server\n", me)); michael@0: michael@0: PR_Lock(client->ml); michael@0: client->operations += 1; michael@0: client->bytesTransferred += 2 * descbytes; michael@0: rv = PR_WaitCondVar(client->stateChange, rand() % clipping); michael@0: PR_Unlock(client->ml); michael@0: if (Aborted(rv)) break; michael@0: } michael@0: michael@0: aborted: michael@0: client->stopped = PR_IntervalNow(); michael@0: michael@0: PR_ClearInterrupt(); michael@0: if (NULL != fd) rv = PR_Close(fd); michael@0: michael@0: PR_Lock(client->ml); michael@0: client->state = cs_exit; michael@0: PR_NotifyCondVar(client->stateChange); michael@0: PR_Unlock(client->ml); michael@0: PR_DELETE(descriptor); michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ALWAYS, michael@0: ("\tClient(0x%p): stopped after %u operations and %u bytes\n", michael@0: PR_GetCurrentThread(), client->operations, client->bytesTransferred)); michael@0: michael@0: } /* Client */ michael@0: michael@0: static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server) michael@0: { michael@0: PRStatus drv, rv; michael@0: char buffer[1024]; michael@0: PRFileDesc *file = NULL; michael@0: PRThread * me = PR_GetCurrentThread(); michael@0: PRInt32 bytes, descbytes, netbytes, filebytes = 0; michael@0: CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); michael@0: PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\tProcessRequest(0x%p): receiving desciptor\n", me)); michael@0: bytes = PR_Recv( michael@0: fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout); michael@0: if (-1 == bytes) michael@0: { michael@0: rv = PR_FAILURE; michael@0: if (Aborted(rv)) goto exit; michael@0: if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\tProcessRequest(0x%p): receive timeout\n", me)); michael@0: } michael@0: goto exit; michael@0: } michael@0: if (0 == bytes) michael@0: { michael@0: rv = PR_FAILURE; michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\tProcessRequest(0x%p): unexpected end of file\n", me)); michael@0: goto exit; michael@0: } michael@0: descbytes = PR_ntohl(descriptor->size); michael@0: TEST_ASSERT(sizeof(*descriptor) == bytes); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n", michael@0: me, descbytes, descriptor->filename)); michael@0: michael@0: file = PR_Open( michael@0: descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666); michael@0: if (NULL == file) michael@0: { michael@0: rv = PR_FAILURE; michael@0: if (Aborted(rv)) goto aborted; michael@0: if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\tProcessRequest(0x%p): open file timeout\n", me)); michael@0: goto aborted; michael@0: } michael@0: } michael@0: TEST_ASSERT(NULL != file); michael@0: michael@0: filebytes = 0; michael@0: while (filebytes < descbytes) michael@0: { michael@0: netbytes = sizeof(buffer); michael@0: if ((descbytes - filebytes) < netbytes) michael@0: netbytes = descbytes - filebytes; michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes)); michael@0: bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); michael@0: if (-1 == bytes) michael@0: { michael@0: rv = PR_FAILURE; michael@0: if (Aborted(rv)) goto aborted; michael@0: if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\t\tProcessRequest(0x%p): receive data timeout\n", me)); michael@0: goto aborted; michael@0: } michael@0: /* michael@0: * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED) michael@0: * on NT here. This is equivalent to ECONNRESET on Unix. michael@0: * -wtc michael@0: */ michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_WARNING, michael@0: ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n", michael@0: me, PR_GetError(), PR_GetOSError())); michael@0: goto aborted; michael@0: } michael@0: if(0 == bytes) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_WARNING, michael@0: ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me)); michael@0: rv = PR_FAILURE; michael@0: goto aborted; michael@0: } michael@0: filebytes += bytes; michael@0: netbytes = bytes; michael@0: /* The byte count for PR_Write should be positive */ michael@0: MY_ASSERT(netbytes > 0); michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes)); michael@0: bytes = PR_Write(file, buffer, netbytes); michael@0: if (netbytes != bytes) michael@0: { michael@0: rv = PR_FAILURE; michael@0: if (Aborted(rv)) goto aborted; michael@0: if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\t\tProcessRequest(0x%p): write file timeout\n", me)); michael@0: goto aborted; michael@0: } michael@0: } michael@0: TEST_ASSERT(bytes > 0); michael@0: } michael@0: michael@0: PR_Lock(server->ml); michael@0: server->operations += 1; michael@0: server->bytesTransferred += filebytes; michael@0: PR_Unlock(server->ml); michael@0: michael@0: rv = PR_Close(file); michael@0: if (Aborted(rv)) goto aborted; michael@0: TEST_ASSERT(PR_SUCCESS == rv); michael@0: file = NULL; michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename)); michael@0: file = PR_Open(descriptor->filename, PR_RDONLY, 0); michael@0: if (NULL == file) michael@0: { michael@0: rv = PR_FAILURE; michael@0: if (Aborted(rv)) goto aborted; michael@0: if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\t\tProcessRequest(0x%p): open file timeout\n", michael@0: PR_GetCurrentThread())); michael@0: goto aborted; michael@0: } michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n", michael@0: me, PR_GetError(), PR_GetOSError())); michael@0: goto aborted; michael@0: } michael@0: TEST_ASSERT(NULL != file); michael@0: michael@0: netbytes = 0; michael@0: while (netbytes < descbytes) michael@0: { michael@0: filebytes = sizeof(buffer); michael@0: if ((descbytes - netbytes) < filebytes) michael@0: filebytes = descbytes - netbytes; michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes)); michael@0: bytes = PR_Read(file, buffer, filebytes); michael@0: if (filebytes != bytes) michael@0: { michael@0: rv = PR_FAILURE; michael@0: if (Aborted(rv)) goto aborted; michael@0: if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\t\tProcessRequest(0x%p): read file timeout\n", me)); michael@0: else michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n", michael@0: me, PR_GetError(), PR_GetOSError())); michael@0: goto aborted; michael@0: } michael@0: TEST_ASSERT(bytes > 0); michael@0: netbytes += bytes; michael@0: filebytes = bytes; michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes)); michael@0: bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout); michael@0: if (filebytes != bytes) michael@0: { michael@0: rv = PR_FAILURE; michael@0: if (Aborted(rv)) goto aborted; michael@0: if (PR_IO_TIMEOUT_ERROR == PR_GetError()) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\t\tProcessRequest(0x%p): send data timeout\n", me)); michael@0: goto aborted; michael@0: } michael@0: break; michael@0: } michael@0: TEST_ASSERT(bytes > 0); michael@0: } michael@0: michael@0: PR_Lock(server->ml); michael@0: server->bytesTransferred += filebytes; michael@0: PR_Unlock(server->ml); michael@0: michael@0: rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH); michael@0: if (Aborted(rv)) goto aborted; michael@0: michael@0: rv = PR_Close(file); michael@0: if (Aborted(rv)) goto aborted; michael@0: TEST_ASSERT(PR_SUCCESS == rv); michael@0: file = NULL; michael@0: michael@0: aborted: michael@0: PR_ClearInterrupt(); michael@0: if (NULL != file) PR_Close(file); michael@0: drv = PR_Delete(descriptor->filename); michael@0: TEST_ASSERT(PR_SUCCESS == drv); michael@0: exit: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\t\tProcessRequest(0x%p): Finished\n", me)); michael@0: michael@0: PR_DELETE(descriptor); michael@0: michael@0: #if defined(WIN95) michael@0: PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */ michael@0: #endif michael@0: return rv; michael@0: } /* ProcessRequest */ michael@0: michael@0: static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool) michael@0: { michael@0: CSWorker_t *worker = PR_NEWZAP(CSWorker_t); michael@0: worker->server = server; michael@0: PR_INIT_CLIST(&worker->element); michael@0: worker->thread = PR_CreateThread( michael@0: PR_USER_THREAD, Worker, worker, michael@0: DEFAULT_SERVER_PRIORITY, thread_scope, michael@0: PR_UNJOINABLE_THREAD, 0); michael@0: if (NULL == worker->thread) michael@0: { michael@0: PR_DELETE(worker); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, michael@0: ("\tCreateWorker(0x%p): create new worker (0x%p)\n", michael@0: PR_GetCurrentThread(), worker->thread)); michael@0: michael@0: return PR_SUCCESS; michael@0: } /* CreateWorker */ michael@0: michael@0: static void PR_CALLBACK Worker(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: PRNetAddr from; michael@0: PRFileDesc *fd = NULL; michael@0: PRThread *me = PR_GetCurrentThread(); michael@0: CSWorker_t *worker = (CSWorker_t*)arg; michael@0: CSServer_t *server = worker->server; michael@0: CSPool_t *pool = &server->pool; michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_NOTICE, michael@0: ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1)); michael@0: michael@0: PR_Lock(server->ml); michael@0: PR_APPEND_LINK(&worker->element, &server->list); michael@0: pool->workers += 1; /* define our existance */ michael@0: michael@0: while (cs_run == server->state) michael@0: { michael@0: while (pool->accepting >= server->workers.accepting) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\t\tWorker(0x%p): waiting for accept slot[%d]\n", michael@0: me, pool->accepting)); michael@0: rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT); michael@0: if (Aborted(rv) || (cs_run != server->state)) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_NOTICE, michael@0: ("\tWorker(0x%p): has been %s\n", michael@0: me, (Aborted(rv) ? "interrupted" : "stopped"))); michael@0: goto exit; michael@0: } michael@0: } michael@0: pool->accepting += 1; /* how many are really in accept */ michael@0: PR_Unlock(server->ml); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\t\tWorker(0x%p): calling accept\n", me)); michael@0: fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT); michael@0: michael@0: PR_Lock(server->ml); michael@0: pool->accepting -= 1; michael@0: PR_NotifyCondVar(pool->acceptComplete); michael@0: michael@0: if ((NULL == fd) && Aborted(PR_FAILURE)) michael@0: { michael@0: if (NULL != server->listener) michael@0: { michael@0: PR_Close(server->listener); michael@0: server->listener = NULL; michael@0: } michael@0: goto exit; michael@0: } michael@0: michael@0: if (NULL != fd) michael@0: { michael@0: /* michael@0: ** Create another worker of the total number of workers is michael@0: ** less than the minimum specified or we have none left in michael@0: ** accept() AND we're not over the maximum. michael@0: ** This sort of presumes that the number allowed in accept michael@0: ** is at least as many as the minimum. Otherwise we'll keep michael@0: ** creating new threads and deleting them soon after. michael@0: */ michael@0: PRBool another = michael@0: ((pool->workers < server->workers.minimum) || michael@0: ((0 == pool->accepting) michael@0: && (pool->workers < server->workers.maximum))) ? michael@0: PR_TRUE : PR_FALSE; michael@0: pool->active += 1; michael@0: PR_Unlock(server->ml); michael@0: michael@0: if (another) (void)CreateWorker(server, pool); michael@0: michael@0: rv = ProcessRequest(fd, server); michael@0: if (PR_SUCCESS != rv) michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ERROR, michael@0: ("\t\tWorker(0x%p): server process ended abnormally\n", me)); michael@0: (void)PR_Close(fd); fd = NULL; michael@0: michael@0: PR_Lock(server->ml); michael@0: pool->active -= 1; michael@0: } michael@0: } michael@0: michael@0: exit: michael@0: PR_ClearInterrupt(); michael@0: PR_Unlock(server->ml); michael@0: michael@0: if (NULL != fd) michael@0: { michael@0: (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH); michael@0: (void)PR_Close(fd); michael@0: } michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_NOTICE, michael@0: ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers)); michael@0: michael@0: PR_Lock(server->ml); michael@0: pool->workers -= 1; /* undefine our existance */ michael@0: PR_REMOVE_AND_INIT_LINK(&worker->element); michael@0: PR_NotifyCondVar(pool->exiting); michael@0: PR_Unlock(server->ml); michael@0: michael@0: PR_DELETE(worker); /* destruction of the "worker" object */ michael@0: michael@0: } /* Worker */ michael@0: michael@0: static void PR_CALLBACK Server(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: PRNetAddr serverAddress; michael@0: PRThread *me = PR_GetCurrentThread(); michael@0: CSServer_t *server = (CSServer_t*)arg; michael@0: PRSocketOptionData sockOpt; michael@0: michael@0: server->listener = PR_Socket(domain, SOCK_STREAM, protocol); michael@0: michael@0: sockOpt.option = PR_SockOpt_Reuseaddr; michael@0: sockOpt.value.reuse_addr = PR_TRUE; michael@0: rv = PR_SetSocketOption(server->listener, &sockOpt); michael@0: TEST_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: memset(&serverAddress, 0, sizeof(serverAddress)); michael@0: if (PR_AF_INET6 != domain) michael@0: rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress); michael@0: else michael@0: rv = PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, DEFAULT_PORT, michael@0: &serverAddress); michael@0: rv = PR_Bind(server->listener, &serverAddress); michael@0: TEST_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: rv = PR_Listen(server->listener, server->backlog); michael@0: TEST_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: server->started = PR_IntervalNow(); michael@0: TimeOfDayMessage("Server started at", me); michael@0: michael@0: PR_Lock(server->ml); michael@0: server->state = cs_run; michael@0: PR_NotifyCondVar(server->stateChange); michael@0: PR_Unlock(server->ml); michael@0: michael@0: /* michael@0: ** Create the first worker (actually, a thread that accepts michael@0: ** connections and then processes the work load as needed). michael@0: ** From this point on, additional worker threads are created michael@0: ** as they are needed by existing worker threads. michael@0: */ michael@0: rv = CreateWorker(server, &server->pool); michael@0: TEST_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: /* michael@0: ** From here on this thread is merely hanging around as the contact michael@0: ** point for the main test driver. It's just waiting for the driver michael@0: ** to declare the test complete. michael@0: */ michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\tServer(0x%p): waiting for state change\n", me)); michael@0: michael@0: PR_Lock(server->ml); michael@0: while ((cs_run == server->state) && !Aborted(rv)) michael@0: { michael@0: rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: PR_Unlock(server->ml); michael@0: PR_ClearInterrupt(); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_INFO, michael@0: ("\tServer(0x%p): shutting down workers\n", me)); michael@0: michael@0: /* michael@0: ** Get all the worker threads to exit. They know how to michael@0: ** clean up after themselves, so this is just a matter of michael@0: ** waiting for clorine in the pool to take effect. During michael@0: ** this stage we're ignoring interrupts. michael@0: */ michael@0: server->workers.minimum = server->workers.maximum = 0; michael@0: michael@0: PR_Lock(server->ml); michael@0: while (!PR_CLIST_IS_EMPTY(&server->list)) michael@0: { michael@0: PRCList *head = PR_LIST_HEAD(&server->list); michael@0: CSWorker_t *worker = (CSWorker_t*)head; michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker)); michael@0: rv = PR_Interrupt(worker->thread); michael@0: TEST_ASSERT(PR_SUCCESS == rv); michael@0: PR_REMOVE_AND_INIT_LINK(head); michael@0: } michael@0: michael@0: while (server->pool.workers > 0) michael@0: { michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_NOTICE, michael@0: ("\tServer(0x%p): waiting for %u workers to exit\n", michael@0: me, server->pool.workers)); michael@0: (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: michael@0: server->state = cs_exit; michael@0: PR_NotifyCondVar(server->stateChange); michael@0: PR_Unlock(server->ml); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ALWAYS, michael@0: ("\tServer(0x%p): stopped after %u operations and %u bytes\n", michael@0: me, server->operations, server->bytesTransferred)); michael@0: michael@0: if (NULL != server->listener) PR_Close(server->listener); michael@0: server->stopped = PR_IntervalNow(); michael@0: michael@0: } /* Server */ michael@0: michael@0: static void WaitForCompletion(PRIntn execution) michael@0: { michael@0: while (execution > 0) michael@0: { michael@0: PRIntn dally = (execution > 30) ? 30 : execution; michael@0: PR_Sleep(PR_SecondsToInterval(dally)); michael@0: if (pthread_stats) PT_FPrintStats(debug_out, "\nPThread Statistics\n"); michael@0: execution -= dally; michael@0: } michael@0: } /* WaitForCompletion */ michael@0: michael@0: static void Help(void) michael@0: { michael@0: PR_fprintf(debug_out, "cltsrv test program usage:\n"); michael@0: PR_fprintf(debug_out, "\t-a threads allowed in accept (5)\n"); michael@0: PR_fprintf(debug_out, "\t-b backlock for listen (5)\n"); michael@0: PR_fprintf(debug_out, "\t-c number of clients to create (1)\n"); michael@0: PR_fprintf(debug_out, "\t-f low water mark for fd caching (0)\n"); michael@0: PR_fprintf(debug_out, "\t-F high water mark for fd caching (0)\n"); michael@0: PR_fprintf(debug_out, "\t-w minimal number of server threads (1)\n"); michael@0: PR_fprintf(debug_out, "\t-W maximum number of server threads (1)\n"); michael@0: PR_fprintf(debug_out, "\t-e duration of the test in seconds (10)\n"); michael@0: PR_fprintf(debug_out, "\t-s dsn name of server (localhost)\n"); michael@0: PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n"); michael@0: PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n"); michael@0: PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n"); michael@0: PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n"); michael@0: PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n"); michael@0: PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n"); michael@0: PR_fprintf(debug_out, "\t-h this message\n"); michael@0: } /* Help */ michael@0: michael@0: static Verbosity IncrementVerbosity(void) michael@0: { michael@0: PRIntn verboge = (PRIntn)verbosity + 1; michael@0: return (Verbosity)verboge; michael@0: } /* IncrementVerbosity */ michael@0: michael@0: int main(int argc, char** argv) michael@0: { michael@0: PRUintn index; michael@0: PRBool boolean; michael@0: CSClient_t *client; michael@0: PRStatus rv, joinStatus; michael@0: CSServer_t *server = NULL; michael@0: michael@0: PRUintn backlog = DEFAULT_BACKLOG; michael@0: PRUintn clients = DEFAULT_CLIENTS; michael@0: const char *serverName = DEFAULT_SERVER; michael@0: PRBool serverIsLocal = PR_TRUE; michael@0: PRUintn accepting = ALLOWED_IN_ACCEPT; michael@0: PRUintn workersMin = DEFAULT_WORKERS_MIN; michael@0: PRUintn workersMax = DEFAULT_WORKERS_MAX; michael@0: PRIntn execution = DEFAULT_EXECUTION_TIME; michael@0: PRIntn low = DEFAULT_LOW, high = DEFAULT_HIGH; michael@0: michael@0: /* michael@0: * -G use global threads michael@0: * -a threads allowed in accept michael@0: * -b backlock for listen michael@0: * -c number of clients to create michael@0: * -f low water mark for caching FDs michael@0: * -F high water mark for caching FDs michael@0: * -w minimal number of server threads michael@0: * -W maximum number of server threads michael@0: * -e duration of the test in seconds michael@0: * -s dsn name of server (implies no server here) michael@0: * -v verbosity michael@0: */ michael@0: michael@0: PLOptStatus os; michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:f:F:w:W:e:s:vdhp"); michael@0: michael@0: debug_out = PR_GetSpecialFD(PR_StandardError); michael@0: michael@0: while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) michael@0: { michael@0: if (PL_OPT_BAD == os) continue; michael@0: switch (opt->option) michael@0: { michael@0: case 'G': /* use global threads */ michael@0: thread_scope = PR_GLOBAL_THREAD; michael@0: break; michael@0: case 'X': /* use XTP as transport */ michael@0: protocol = 36; michael@0: break; michael@0: case '6': /* Use IPv6 */ michael@0: domain = PR_AF_INET6; michael@0: break; michael@0: case 'a': /* the value for accepting */ michael@0: accepting = atoi(opt->value); michael@0: break; michael@0: case 'b': /* the value for backlock */ michael@0: backlog = atoi(opt->value); michael@0: break; michael@0: case 'c': /* number of client threads */ michael@0: clients = atoi(opt->value); michael@0: break; michael@0: case 'f': /* low water fd cache */ michael@0: low = atoi(opt->value); michael@0: break; michael@0: case 'F': /* low water fd cache */ michael@0: high = atoi(opt->value); michael@0: break; michael@0: case 'w': /* minimum server worker threads */ michael@0: workersMin = atoi(opt->value); michael@0: break; michael@0: case 'W': /* maximum server worker threads */ michael@0: workersMax = atoi(opt->value); michael@0: break; michael@0: case 'e': /* program execution time in seconds */ michael@0: execution = atoi(opt->value); michael@0: break; michael@0: case 's': /* server's address */ michael@0: serverName = opt->value; michael@0: break; michael@0: case 'v': /* verbosity */ michael@0: verbosity = IncrementVerbosity(); michael@0: break; michael@0: case 'd': /* debug mode */ michael@0: debug_mode = PR_TRUE; michael@0: break; michael@0: case 'p': /* pthread mode */ michael@0: pthread_stats = PR_TRUE; michael@0: break; michael@0: case 'h': michael@0: default: michael@0: Help(); michael@0: return 2; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: michael@0: if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) serverIsLocal = PR_FALSE; michael@0: if (0 == execution) execution = DEFAULT_EXECUTION_TIME; michael@0: if (0 == workersMax) workersMax = DEFAULT_WORKERS_MAX; michael@0: if (0 == workersMin) workersMin = DEFAULT_WORKERS_MIN; michael@0: if (0 == accepting) accepting = ALLOWED_IN_ACCEPT; michael@0: if (0 == backlog) backlog = DEFAULT_BACKLOG; michael@0: michael@0: if (workersMin > accepting) accepting = workersMin; michael@0: michael@0: PR_STDIO_INIT(); michael@0: TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread()); michael@0: michael@0: cltsrv_log_file = PR_NewLogModule("cltsrv_log"); michael@0: MY_ASSERT(NULL != cltsrv_log_file); michael@0: boolean = PR_SetLogFile("cltsrv.log"); michael@0: MY_ASSERT(boolean); michael@0: michael@0: rv = PR_SetFDCacheSize(low, high); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: michael@0: if (serverIsLocal) michael@0: { michael@0: /* Establish the server */ michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_INFO, michael@0: ("main(0x%p): starting server\n", PR_GetCurrentThread())); michael@0: michael@0: server = PR_NEWZAP(CSServer_t); michael@0: PR_INIT_CLIST(&server->list); michael@0: server->state = cs_init; michael@0: server->ml = PR_NewLock(); michael@0: server->backlog = backlog; michael@0: server->port = DEFAULT_PORT; michael@0: server->workers.minimum = workersMin; michael@0: server->workers.maximum = workersMax; michael@0: server->workers.accepting = accepting; michael@0: server->stateChange = PR_NewCondVar(server->ml); michael@0: server->pool.exiting = PR_NewCondVar(server->ml); michael@0: server->pool.acceptComplete = PR_NewCondVar(server->ml); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_NOTICE, michael@0: ("main(0x%p): creating server thread\n", PR_GetCurrentThread())); michael@0: michael@0: server->thread = PR_CreateThread( michael@0: PR_USER_THREAD, Server, server, PR_PRIORITY_HIGH, michael@0: thread_scope, PR_JOINABLE_THREAD, 0); michael@0: TEST_ASSERT(NULL != server->thread); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("main(0x%p): waiting for server init\n", PR_GetCurrentThread())); michael@0: michael@0: PR_Lock(server->ml); michael@0: while (server->state == cs_init) michael@0: PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); michael@0: PR_Unlock(server->ml); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("main(0x%p): server init complete (port #%d)\n", michael@0: PR_GetCurrentThread(), server->port)); michael@0: } michael@0: michael@0: if (clients != 0) michael@0: { michael@0: /* Create all of the clients */ michael@0: PRHostEnt host; michael@0: char buffer[BUFFER_SIZE]; michael@0: client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t)); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("main(0x%p): creating %d client threads\n", michael@0: PR_GetCurrentThread(), clients)); michael@0: michael@0: if (!serverIsLocal) michael@0: { michael@0: rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host); michael@0: if (PR_SUCCESS != rv) michael@0: { michael@0: PL_FPrintError(PR_STDERR, "PR_GetHostByName"); michael@0: return 2; michael@0: } michael@0: } michael@0: michael@0: for (index = 0; index < clients; ++index) michael@0: { michael@0: client[index].state = cs_init; michael@0: client[index].ml = PR_NewLock(); michael@0: if (serverIsLocal) michael@0: { michael@0: if (PR_AF_INET6 != domain) michael@0: (void)PR_InitializeNetAddr( michael@0: PR_IpAddrLoopback, DEFAULT_PORT, michael@0: &client[index].serverAddress); michael@0: else michael@0: rv = PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6, michael@0: DEFAULT_PORT, &client[index].serverAddress); michael@0: } michael@0: else michael@0: { michael@0: (void)PR_EnumerateHostEnt( michael@0: 0, &host, DEFAULT_PORT, &client[index].serverAddress); michael@0: } michael@0: client[index].stateChange = PR_NewCondVar(client[index].ml); michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_INFO, michael@0: ("main(0x%p): creating client threads\n", PR_GetCurrentThread())); michael@0: client[index].thread = PR_CreateThread( michael@0: PR_USER_THREAD, Client, &client[index], PR_PRIORITY_NORMAL, michael@0: thread_scope, PR_JOINABLE_THREAD, 0); michael@0: TEST_ASSERT(NULL != client[index].thread); michael@0: PR_Lock(client[index].ml); michael@0: while (cs_init == client[index].state) michael@0: PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); michael@0: PR_Unlock(client[index].ml); michael@0: } michael@0: } michael@0: michael@0: /* Then just let them go at it for a bit */ michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ALWAYS, michael@0: ("main(0x%p): waiting for execution interval (%d seconds)\n", michael@0: PR_GetCurrentThread(), execution)); michael@0: michael@0: WaitForCompletion(execution); michael@0: michael@0: TimeOfDayMessage("Shutting down", PR_GetCurrentThread()); michael@0: michael@0: if (clients != 0) michael@0: { michael@0: for (index = 0; index < clients; ++index) michael@0: { michael@0: TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, michael@0: ("main(0x%p): notifying client(0x%p) to stop\n", michael@0: PR_GetCurrentThread(), client[index].thread)); michael@0: michael@0: PR_Lock(client[index].ml); michael@0: if (cs_run == client[index].state) michael@0: { michael@0: client[index].state = cs_stop; michael@0: PR_Interrupt(client[index].thread); michael@0: while (cs_stop == client[index].state) michael@0: PR_WaitCondVar( michael@0: client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: PR_Unlock(client[index].ml); michael@0: michael@0: TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE, michael@0: ("main(0x%p): joining client(0x%p)\n", michael@0: PR_GetCurrentThread(), client[index].thread)); michael@0: michael@0: joinStatus = PR_JoinThread(client[index].thread); michael@0: TEST_ASSERT(PR_SUCCESS == joinStatus); michael@0: PR_DestroyCondVar(client[index].stateChange); michael@0: PR_DestroyLock(client[index].ml); michael@0: } michael@0: PR_DELETE(client); michael@0: } michael@0: michael@0: if (NULL != server) michael@0: { michael@0: /* All clients joined - retrieve the server */ michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_NOTICE, michael@0: ("main(0x%p): notifying server(0x%p) to stop\n", michael@0: PR_GetCurrentThread(), server->thread)); michael@0: michael@0: PR_Lock(server->ml); michael@0: server->state = cs_stop; michael@0: PR_Interrupt(server->thread); michael@0: while (cs_exit != server->state) michael@0: PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); michael@0: PR_Unlock(server->ml); michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_NOTICE, michael@0: ("main(0x%p): joining server(0x%p)\n", michael@0: PR_GetCurrentThread(), server->thread)); michael@0: joinStatus = PR_JoinThread(server->thread); michael@0: TEST_ASSERT(PR_SUCCESS == joinStatus); michael@0: michael@0: PR_DestroyCondVar(server->stateChange); michael@0: PR_DestroyCondVar(server->pool.exiting); michael@0: PR_DestroyCondVar(server->pool.acceptComplete); michael@0: PR_DestroyLock(server->ml); michael@0: PR_DELETE(server); michael@0: } michael@0: michael@0: TEST_LOG( michael@0: cltsrv_log_file, TEST_LOG_ALWAYS, michael@0: ("main(0x%p): test complete\n", PR_GetCurrentThread())); michael@0: michael@0: PT_FPrintStats(debug_out, "\nPThread Statistics\n"); michael@0: michael@0: TimeOfDayMessage("Test exiting at", PR_GetCurrentThread()); michael@0: PR_Cleanup(); michael@0: return 0; michael@0: } /* main */ michael@0: michael@0: /* cltsrv.c */