Merged the new socket poll fd functions into NSPRPUB_RELEASE_3_1_BRANCH
authorwtc%netscape.com
Mon, 05 Apr 1999 23:39:31 +0000
branchNSPRPUB_RELEASE_3_1_BRANCH
changeset 532 a4aaf42b8ad4aa1a5fe74cab2d6d0dde5cd4f99c
parent 520 3d016009f5a6dbc571e8db319b4063b188dac184
child 533 7d71894fcecb6cdca0e9f76c4e40d4e09be9ae5f
push idunknown
push userunknown
push dateunknown
Merged the new socket poll fd functions into NSPRPUB_RELEASE_3_1_BRANCH. Modified files: pprio.h, prsocket.c, ptio.c, pr/tests/Makefile, pr/tests/prpoll.c.
pr/include/private/pprio.h
pr/src/io/prsocket.c
pr/src/pthreads/ptio.c
pr/tests/Makefile
pr/tests/prpoll.c
--- a/pr/include/private/pprio.h
+++ b/pr/include/private/pprio.h
@@ -65,16 +65,56 @@ PR_EXTERN(PRFileDesc*)  PR_AllocFileDesc
 PR_EXTERN(void)         PR_FreeFileDesc(PRFileDesc *fd);
 /*
 ** Import an existing OS file to NSPR. 
 */
 PR_EXTERN(PRFileDesc*)  PR_ImportFile(PRInt32 osfd);
 PR_EXTERN(PRFileDesc*)  PR_ImportTCPSocket(PRInt32 osfd);
 PR_EXTERN(PRFileDesc*)  PR_ImportUDPSocket(PRInt32 osfd);
 
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_CreateSocketPollFd
+ * DESCRIPTION:
+ *     Create a PRFileDesc wrapper for a native socket handle, for use with
+ *	   PR_Poll only
+ * INPUTS:
+ *     None
+ * OUTPUTS:
+ *     None
+ * RETURN: PRFileDesc*
+ *     Upon successful completion, PR_CreateSocketPollFd returns a pointer
+ *     to the PRFileDesc created for the native socket handle
+ *     Returns a NULL pointer if the create of a new PRFileDesc failed
+ *
+ **************************************************************************
+ */
+
+PR_EXTERN(PRFileDesc*)	PR_CreateSocketPollFd(PRInt32 osfd);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_DestroySocketPollFd
+ * DESCRIPTION:
+ *     Destroy the PRFileDesc wrapper created by PR_CreateSocketPollFd
+ * INPUTS:
+ *     None
+ * OUTPUTS:
+ *     None
+ * RETURN: PRFileDesc*
+ *     Upon successful completion, PR_DestroySocketPollFd returns
+ *	   PR_SUCCESS, else PR_FAILURE
+ *
+ **************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd);
+
+
 /*
 ** Macros for PR_Socket
 **
 ** Socket types: PR_SOCK_STREAM, PR_SOCK_DGRAM
 */
 
 #ifdef WIN32
 
--- a/pr/src/io/prsocket.c
+++ b/pr/src/io/prsocket.c
@@ -184,16 +184,51 @@ PRFileDesc *fd;
 	fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods());
 	if (fd != NULL)
 		_PR_MD_MAKE_NONBLOCK(fd);
 	else
 		_PR_MD_CLOSE_SOCKET(osfd);
 	return(fd);
 }
 
+
+static const PRIOMethods* PR_GetSocketPollFdMethods();
+
+PR_IMPLEMENT(PRFileDesc*) PR_CreateSocketPollFd(PRInt32 osfd)
+{
+    PRFileDesc *fd;
+
+    if (!_pr_initialized) _PR_ImplicitInitialization();
+
+    fd = _PR_Getfd();
+
+    if (fd == NULL) PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+    else
+    {
+        fd->secret->md.osfd = osfd;
+        fd->secret->inheritable = PR_FALSE;
+    	fd->secret->state = _PR_FILEDESC_OPEN;
+        fd->methods = PR_GetSocketPollFdMethods();
+    }
+
+    return fd;
+}  /* PR_CreateSocketPollFD */
+
+PR_IMPLEMENT(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd)
+{
+    if (NULL == fd)
+    {
+        PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+        return PR_FAILURE;
+    }
+    fd->secret->state = _PR_FILEDESC_CLOSED;
+    _PR_Putfd(fd);
+    return PR_SUCCESS;
+}  /* PR_DestroySocketPollFd */
+
 static PRStatus PR_CALLBACK SocketConnect(
     PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
 {
 	PRInt32 rv;    /* Return value of _PR_MD_CONNECT */
 	PRThread *me = _PR_MD_CURRENT_THREAD();
 
 	if (_PR_PENDING_INTERRUPT(me)) {
 		me->flags &= ~_PR_INTERRUPT;
@@ -1080,26 +1115,66 @@ static PRIOMethods udpMethods = {
 	SocketGetName,
 	SocketGetPeerName,
 	SocketGetSockOpt,
 	SocketSetSockOpt,
 	_PR_SocketGetSocketOption,
 	_PR_SocketSetSocketOption
 };
 
+
+static PRIOMethods socketpollfdMethods = {
+    (PRDescType) 0,
+    (PRCloseFN)_PR_InvalidStatus,
+    (PRReadFN)_PR_InvalidInt,
+    (PRWriteFN)_PR_InvalidInt,
+    (PRAvailableFN)_PR_InvalidInt,
+    (PRAvailable64FN)_PR_InvalidInt64,
+    (PRFsyncFN)_PR_InvalidStatus,
+    (PRSeekFN)_PR_InvalidInt,
+    (PRSeek64FN)_PR_InvalidInt64,
+    (PRFileInfoFN)_PR_InvalidStatus,
+    (PRFileInfo64FN)_PR_InvalidStatus,
+    (PRWritevFN)_PR_InvalidInt,        
+    (PRConnectFN)_PR_InvalidStatus,        
+    (PRAcceptFN)_PR_InvalidDesc,        
+    (PRBindFN)_PR_InvalidStatus,        
+    (PRListenFN)_PR_InvalidStatus,        
+    (PRShutdownFN)_PR_InvalidStatus,    
+    (PRRecvFN)_PR_InvalidInt,        
+    (PRSendFN)_PR_InvalidInt,        
+    (PRRecvfromFN)_PR_InvalidInt,    
+    (PRSendtoFN)_PR_InvalidInt,        
+	SocketPoll,
+    (PRAcceptreadFN)_PR_InvalidInt,   
+    (PRTransmitfileFN)_PR_InvalidInt, 
+    (PRGetsocknameFN)_PR_InvalidStatus,    
+    (PRGetpeernameFN)_PR_InvalidStatus,    
+    (PRGetsockoptFN)_PR_InvalidStatus,    
+    (PRSetsockoptFN)_PR_InvalidStatus,    
+    (PRGetsocketoptionFN)_PR_InvalidStatus,
+    (PRSetsocketoptionFN)_PR_InvalidStatus
+};
+
 PR_IMPLEMENT(const PRIOMethods*) PR_GetTCPMethods()
 {
 	return &tcpMethods;
 }
 
 PR_IMPLEMENT(const PRIOMethods*) PR_GetUDPMethods()
 {
 	return &udpMethods;
 }
 
+static const PRIOMethods* PR_GetSocketPollFdMethods()
+{
+    return &socketpollfdMethods;
+}  /* PR_GetSocketPollFdMethods */
+
+
 PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto)
 {
 	PRInt32 osfd;
 	int one = 1;
 	PRFileDesc *fd;
 
 	if (!_pr_initialized) _PR_ImplicitInitialization();
 	if (AF_INET != domain
--- a/pr/src/pthreads/ptio.c
+++ b/pr/src/pthreads/ptio.c
@@ -2733,16 +2733,50 @@ static PRIOMethods _pr_udp_methods = {
     pt_GetSockName,
     pt_GetPeerName,
     pt_GetSockOpt,
     pt_SetSockOpt,
     pt_GetSocketOption,
     pt_SetSocketOption
 };
 
+
+static PRIOMethods _pr_socketpollfd_methods = {
+    (PRDescType) 0,
+    (PRCloseFN)_PR_InvalidStatus,
+    (PRReadFN)_PR_InvalidInt,
+    (PRWriteFN)_PR_InvalidInt,
+    (PRAvailableFN)_PR_InvalidInt,
+    (PRAvailable64FN)_PR_InvalidInt64,
+    (PRFsyncFN)_PR_InvalidStatus,
+    (PRSeekFN)_PR_InvalidInt,
+    (PRSeek64FN)_PR_InvalidInt64,
+    (PRFileInfoFN)_PR_InvalidStatus,
+    (PRFileInfo64FN)_PR_InvalidStatus,
+    (PRWritevFN)_PR_InvalidInt,        
+    (PRConnectFN)_PR_InvalidStatus,        
+    (PRAcceptFN)_PR_InvalidDesc,        
+    (PRBindFN)_PR_InvalidStatus,        
+    (PRListenFN)_PR_InvalidStatus,        
+    (PRShutdownFN)_PR_InvalidStatus,    
+    (PRRecvFN)_PR_InvalidInt,        
+    (PRSendFN)_PR_InvalidInt,        
+    (PRRecvfromFN)_PR_InvalidInt,    
+    (PRSendtoFN)_PR_InvalidInt,        
+	pt_Poll,
+    (PRAcceptreadFN)_PR_InvalidInt,   
+    (PRTransmitfileFN)_PR_InvalidInt, 
+    (PRGetsocknameFN)_PR_InvalidStatus,    
+    (PRGetpeernameFN)_PR_InvalidStatus,    
+    (PRGetsockoptFN)_PR_InvalidStatus,    
+    (PRSetsockoptFN)_PR_InvalidStatus,    
+    (PRGetsocketoptionFN)_PR_InvalidStatus,
+    (PRSetsocketoptionFN)_PR_InvalidStatus
+};
+
 #if defined(_PR_FCNTL_FLAGS)
 #undef _PR_FCNTL_FLAGS
 #endif
 
 #if defined(HPUX) || defined(OSF1) || defined(SOLARIS) || defined (IRIX) \
     || defined(AIX) || defined(LINUX) || defined(FREEBSD) || defined(NETBSD) \
     || defined(OPENBSD) || defined(BSDI)
 #define _PR_FCNTL_FLAGS O_NONBLOCK
@@ -2799,16 +2833,21 @@ PR_IMPLEMENT(const PRIOMethods*) PR_GetT
     return &_pr_tcp_methods;
 }  /* PR_GetTCPMethods */
 
 PR_IMPLEMENT(const PRIOMethods*) PR_GetUDPMethods()
 {
     return &_pr_udp_methods;
 }  /* PR_GetUDPMethods */
 
+static const PRIOMethods* PR_GetSocketPollFdMethods()
+{
+    return &_pr_socketpollfd_methods;
+}  /* PR_GetSocketPollFdMethods */
+
 PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc(
     PRInt32 osfd, const PRIOMethods *methods)
 {
     PRFileDesc *fd = _PR_Getfd();
 
     /*
      * Assert that the file descriptor is small enough to fit in the
      * fd_set passed to select
@@ -3519,16 +3558,48 @@ PR_IMPLEMENT(PRFileDesc*) PR_ImportUDPSo
     PRFileDesc *fd;
 
     if (!_pr_initialized) _PR_ImplicitInitialization();
     fd = pt_SetMethods(osfd, PR_DESC_SOCKET_UDP);
     if (NULL != fd) close(osfd);
     return fd;
 }  /* PR_ImportUDPSocket */
 
+PR_IMPLEMENT(PRFileDesc*) PR_CreateSocketPollFd(PRInt32 osfd)
+{
+    PRFileDesc *fd;
+
+    if (!_pr_initialized) _PR_ImplicitInitialization();
+
+    fd = _PR_Getfd();
+
+    if (fd == NULL) PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+    else
+    {
+        fd->secret->md.osfd = osfd;
+        fd->secret->inheritable = PR_FALSE;
+    	fd->secret->state = _PR_FILEDESC_OPEN;
+        fd->methods = PR_GetSocketPollFdMethods();
+    }
+
+    return fd;
+}  /* PR_CreateSocketPollFD */
+
+PR_IMPLEMENT(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd)
+{
+    if (NULL == fd)
+    {
+        PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+        return PR_FAILURE;
+    }
+    fd->secret->state = _PR_FILEDESC_CLOSED;
+    _PR_Putfd(fd);
+    return PR_SUCCESS;
+}  /* PR_DestroySocketPollFd */
+
 PR_IMPLEMENT(PRInt32) PR_FileDesc2NativeHandle(PRFileDesc *bottom)
 {
     PRInt32 osfd = -1;
     bottom = (NULL == bottom) ?
         NULL : PR_GetIdentitiesLayer(bottom, PR_NSPR_IO_LAYER);
     if (NULL == bottom) PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
     else osfd = bottom->secret->md.osfd;
     return osfd;
--- a/pr/tests/Makefile
+++ b/pr/tests/Makefile
@@ -426,24 +426,16 @@ ifeq ($(AIX_PRE_4_2),1)
 	$(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(DIST)/lib/libnspr$(MOD_VERSION).a
 	$(CC) -o $@ $(AIX_TMP) $(AIX_WRAP)
 	rm -f $(AIX_TMP)
 
 else
 
 # All platforms that are not AIX pre-4.2.
 
-
-ifeq ($(USE_PTHREADS), 1)
-$(OBJDIR)/attach: $(OBJDIR)/attach.$(OBJ_SUFFIX)
-	@$(MAKE_OBJDIR)
-	$(CC) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
-endif
-
-
 $(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX)
 	@$(MAKE_OBJDIR)
 ifeq ($(OS_ARCH), WINNT)
 ifeq ($(OS_TARGET),WIN16)
 	echo system windows >w16link
 	echo name $@  >>w16link
 	echo option map >>w16link
 #	echo option CASEEXACT >>w16link
@@ -470,17 +462,28 @@ endif
 
 endif
 
 export:: $(TARGETS)
 install:: export
 clean::
 	rm -f $(TARGETS)
 
+# The prpoll test calls BSD socket functions, so it needs to link
+# with -lsocket on some platforms.
+ifeq ($(OS_ARCH),SunOS)
+ifneq ($(OS_RELEASE),4.1.3_U1)
+$(OBJDIR)/prpoll: $(OBJDIR)/prpoll.o
+	$(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBPR) -lsocket $(EXTRA_LIBS) -o $@
+endif
+endif
+
 ifeq ($(USE_PTHREADS), 1)
+$(OBJDIR)/attach: $(OBJDIR)/attach.o
+	$(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
 $(OBJDIR)/foreign: $(OBJDIR)/foreign.o
 	$(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
 $(OBJDIR)/provider: $(OBJDIR)/provider.o
 	$(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
 $(OBJDIR)/socket: $(OBJDIR)/socket.o
 	$(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
 $(OBJDIR)/testfile: $(OBJDIR)/testfile.o
 	$(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
--- a/pr/tests/prpoll.c
+++ b/pr/tests/prpoll.c
@@ -11,39 +11,46 @@
  * NPL.
  * 
  * The Initial Developer of this code under the NPL is Netscape
  * Communications Corporation.  Portions created by Netscape are
  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  * Reserved.
  */
 
+#ifdef WIN32
+#include <windows.h>
+#endif
+
 #include "prinit.h"
 #include "prio.h"
 #include "prlog.h"
 #include "prprf.h"
 #include "prnetdb.h"
 
 #ifndef XP_MAC
 #include "private/pprio.h"
 #else
 #include "pprio.h"
 #endif
 
+#define CLIENT_LOOPS	5
+#define BUF_SIZE		128
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 
 static void
 clientThreadFunc(void *arg)
 {
     PRUint16 port = (PRUint16) arg;
     PRFileDesc *sock;
     PRNetAddr addr;
-    char buf[128];
+    char buf[BUF_SIZE];
     int i;
 
     addr.inet.family = PR_AF_INET;
     addr.inet.port = PR_htons(port);
     addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
     PR_snprintf(buf, sizeof(buf), "%hu", port);
 
     for (i = 0; i < 5; i++) {
@@ -56,21 +63,26 @@ clientThreadFunc(void *arg)
 }
 
 int main(int argc, char **argv)
 {
     PRFileDesc *listenSock1, *listenSock2;
     PRFileDesc *badFD;
     PRUint16 listenPort1, listenPort2;
     PRNetAddr addr;
-    char buf[128];
+    char buf[BUF_SIZE];
     PRThread *clientThread;
     PRPollDesc pds0[10], pds1[10], *pds, *other_pds;
     PRIntn npds;
     PRInt32 retVal;
+    PRInt32 sd, rv;
+	struct sockaddr_in saddr;
+    PRIntn saddr_len;
+    PRUint16 listenPort3;
+    PRFileDesc *socket_poll_fd;
     PRIntn i, j;
 
     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
     PR_STDIO_INIT();
 
     printf("This program tests PR_Poll with sockets.\n");
     printf("Timeout, error reporting, and normal operation are tested.\n\n");
 
@@ -111,30 +123,52 @@ int main(int argc, char **argv)
 	fprintf(stderr, "PR_GetSockName failed\n");
 	exit(1);
     }
     listenPort2 = PR_ntohs(addr.inet.port);
     if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
 	fprintf(stderr, "Can't listen on a socket\n");
 	exit(1);
     }
-    PR_snprintf(buf, sizeof(buf),
-	    "The server thread is listening on ports %hu and %hu\n\n",
-	    listenPort1, listenPort2);
-    printf("%s", buf);
-
     /* Set up the poll descriptor array */
     pds = pds0;
     other_pds = pds1;
     memset(pds, 0, sizeof(pds));
-    pds[0].fd = listenSock1;
-    pds[0].in_flags = PR_POLL_READ;
-    pds[1].fd = listenSock2;
-    pds[1].in_flags = PR_POLL_READ;
-    npds = 2;
+	npds = 0;
+    pds[npds].fd = listenSock1;
+    pds[npds].in_flags = PR_POLL_READ;
+	npds++;
+    pds[npds].fd = listenSock2;
+    pds[npds].in_flags = PR_POLL_READ;
+	npds++;
+
+	sd = socket(AF_INET, SOCK_STREAM, 0);
+	PR_ASSERT(sd >= 0);
+	memset((char *) &saddr, 0, sizeof(saddr));
+	saddr.sin_family = AF_INET;
+	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+	saddr.sin_port = htons(0);
+
+	rv = bind(sd, (struct sockaddr *)&saddr, sizeof(saddr));
+	PR_ASSERT(rv == 0);
+	saddr_len = sizeof(saddr);
+	rv = getsockname(sd, (struct sockaddr *) &saddr, &saddr_len);
+	PR_ASSERT(rv == 0);
+    listenPort3 = ntohs(saddr.sin_port);
+
+	rv = listen(sd, 5);
+	PR_ASSERT(rv == 0);
+    pds[npds].fd = socket_poll_fd = PR_CreateSocketPollFd(sd);
+	PR_ASSERT(pds[npds].fd);
+    pds[npds].in_flags = PR_POLL_READ;
+    npds++;
+    PR_snprintf(buf, sizeof(buf),
+	    "The server thread is listening on ports %hu, %hu and %hu\n\n",
+	    listenPort1, listenPort2, listenPort3);
+    printf("%s", buf);
 
     /* Testing timeout */
     printf("PR_Poll should time out in 5 seconds\n");
     retVal = PR_Poll(pds, npds, PR_SecondsToInterval(5));
     if (retVal != 0) {
 	PR_snprintf(buf, sizeof(buf),
 		"PR_Poll should time out and return 0, but it returns %ld\n",
 		retVal);
@@ -145,129 +179,172 @@ int main(int argc, char **argv)
 
     /* Testing bad fd */
     printf("PR_Poll should detect a bad file descriptor\n");
     if ((badFD = PR_NewTCPSocket()) == NULL) {
 	fprintf(stderr, "Can't create a TCP socket\n");
 	exit(1);
     }
 
-    pds[2].fd = badFD;
-    pds[2].in_flags = PR_POLL_READ;
-    npds = 3;
+    pds[npds].fd = badFD;
+    pds[npds].in_flags = PR_POLL_READ;
+    npds++;
     PR_Close(badFD);  /* make the fd bad */
+#if 0
     retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
     if (retVal != 1 || (unsigned short) pds[2].out_flags != PR_POLL_NVAL) {
 	fprintf(stderr, "Failed to detect the bad fd: "
 		"PR_Poll returns %d, out_flags is 0x%hx\n",
-		retVal, pds[2].out_flags);
+		retVal, pds[npds - 1].out_flags);
 	exit(1);
     }
     printf("PR_Poll detected the bad fd.  Test passed.\n\n");
-    npds = 2;
+#endif
+    npds--;
 
     clientThread = PR_CreateThread(PR_USER_THREAD,
 	    clientThreadFunc, (void *) listenPort1,
 	    PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
 	    PR_UNJOINABLE_THREAD, 0);
     if (clientThread == NULL) {
 	fprintf(stderr, "can't create thread\n");
 	exit(1);
     }
 
     clientThread = PR_CreateThread(PR_USER_THREAD,
 	    clientThreadFunc, (void *) listenPort2,
-	    PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+	    PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+	    PR_UNJOINABLE_THREAD, 0);
+    if (clientThread == NULL) {
+	fprintf(stderr, "can't create thread\n");
+	exit(1);
+    }
+
+    clientThread = PR_CreateThread(PR_USER_THREAD,
+	    clientThreadFunc, (void *) listenPort3,
+	    PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD,
 	    PR_UNJOINABLE_THREAD, 0);
     if (clientThread == NULL) {
 	fprintf(stderr, "can't create thread\n");
 	exit(1);
     }
 
-    printf("Two client threads are created.  Each of them will\n");
-    printf("send data to one of the two ports the server is listening on.\n");
+
+    printf("Three client threads are created.  Each of them will\n");
+    printf("send data to one of the three ports the server is listening on.\n");
     printf("The data they send is the port number.  Each of them send\n");
     printf("the data five times, so you should see ten lines below,\n");
     printf("interleaved in an arbitrary order.\n");
 
-    /* 20 events total */
+    /* 30 events total */
     i = 0;
-    while (i < 20) {
-	PRPollDesc *tmp;
-	int nextIndex;
-	int nEvents = 0;
+    while (i < 30) {
+		PRPollDesc *tmp;
+		int nextIndex;
+		int nEvents = 0;
 
-	retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
-	PR_ASSERT(retVal != 0);  /* no timeout */
-	if (retVal == -1) {
-	    fprintf(stderr, "PR_Poll failed\n");
-	    exit(1);
-	}
+		retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
+		PR_ASSERT(retVal != 0);  /* no timeout */
+		if (retVal == -1) {
+			fprintf(stderr, "PR_Poll failed\n");
+			exit(1);
+		}
+
+		nextIndex = 3;
+		/* the three listening sockets */
+		for (j = 0; j < 3; j++) {
+			other_pds[j] = pds[j];
+			PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+				&& (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+			if (pds[j].out_flags & PR_POLL_READ) {
+				PRFileDesc *sock;
 
-	nextIndex = 2;
-	/* the two listening sockets */
-	for (j = 0; j < 2; j++) {
-	    other_pds[j] = pds[j];
-	    PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
-		    && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
-	    if (pds[j].out_flags & PR_POLL_READ) {
-		PRFileDesc *sock;
-
-		nEvents++;
-		sock = PR_Accept(pds[j].fd, NULL, PR_INTERVAL_NO_TIMEOUT);
-		if (sock == NULL) {
-		    fprintf(stderr, "PR_Accept() failed\n");
-		    exit(1);
+				nEvents++;
+				if (j == 2) {
+					int newsd;
+					newsd = accept(PR_FileDesc2NativeHandle(pds[j].fd), NULL, 0);
+					if (newsd == -1) {
+						fprintf(stderr, "accept() failed\n");
+						exit(1);
+					}
+					other_pds[nextIndex].fd  = PR_CreateSocketPollFd(newsd);
+					PR_ASSERT(other_pds[nextIndex].fd);
+					other_pds[nextIndex].in_flags = PR_POLL_READ;
+				} else {
+					sock = PR_Accept(pds[j].fd, NULL, PR_INTERVAL_NO_TIMEOUT);
+					if (sock == NULL) {
+						fprintf(stderr, "PR_Accept() failed\n");
+						exit(1);
+					}
+					other_pds[nextIndex].fd = sock;
+					other_pds[nextIndex].in_flags = PR_POLL_READ;
+				}
+				nextIndex++;
+			} else if (pds[j].out_flags & PR_POLL_ERR) {
+				fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+				exit(1);
+			} else if (pds[j].out_flags & PR_POLL_NVAL) {
+				fprintf(stderr, "PR_Poll() indicates that fd %d is invalid\n",
+					PR_FileDesc2NativeHandle(pds[j].fd));
+				exit(1);
+			}
 		}
-		other_pds[nextIndex].fd = sock;
-		other_pds[nextIndex].in_flags = PR_POLL_READ;
-		nextIndex++;
-	    } else if (pds[j].out_flags & PR_POLL_ERR) {
-		fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
-		exit(1);
-	    } else if (pds[j].out_flags & PR_POLL_NVAL) {
-		fprintf(stderr, "PR_Poll() indicates that fd %d is invalid\n",
-			PR_FileDesc2NativeHandle(pds[j].fd));
-		exit(1);
-	    }
-	}
+
+		for (j = 3; j < npds; j++) {
+			PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+				&& (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+			if (pds[j].out_flags & PR_POLL_READ) {
+				PRInt32 nBytes;
 
-	for (j = 2; j < npds; j++) {
-	    PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
-		    && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
-	    if (pds[j].out_flags & PR_POLL_READ) {
-		PRInt32 nBytes;
-
-		nEvents++;
-		nBytes = PR_Read(pds[j].fd, buf, sizeof(buf));
-		if (nBytes == -1) {
-		    fprintf(stderr, "PR_Read() failed\n");
-		    exit(1);
+				nEvents++;
+				/* XXX: This call is a hack and should be fixed */
+				if (PR_GetDescType(pds[j].fd) == (PRDescType) 0) {
+					nBytes = recv(PR_FileDesc2NativeHandle(pds[j].fd), buf,
+										sizeof(buf), 0);
+					if (nBytes == -1) {
+						fprintf(stderr, "recv() failed\n");
+						exit(1);
+					}
+					printf("Server read %d bytes from native fd %d\n",nBytes,
+										PR_FileDesc2NativeHandle(pds[j].fd));
+#ifdef WIN32
+					closesocket((SOCKET)PR_FileDesc2NativeHandle(pds[j].fd));
+#else
+					close(PR_FileDesc2NativeHandle(pds[j].fd));
+#endif
+					PR_DestroySocketPollFd(pds[j].fd);
+				} else {
+					nBytes = PR_Read(pds[j].fd, buf, sizeof(buf));
+					if (nBytes == -1) {
+						fprintf(stderr, "PR_Read() failed\n");
+						exit(1);
+					}
+					PR_Close(pds[j].fd);
+				}
+				/* Just to be safe */
+				buf[BUF_SIZE - 1] = '\0';
+				printf("The server received \"%s\" from a client\n", buf);
+			} else if (pds[j].out_flags & PR_POLL_ERR) {
+				fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+				exit(1);
+			} else if (pds[j].out_flags & PR_POLL_NVAL) {
+				fprintf(stderr, "PR_Poll() indicates that an fd is invalid\n");
+				exit(1);
+			} else {
+				other_pds[nextIndex] = pds[j];
+				nextIndex++;
+			}
 		}
-		/* Just to be safe */
-		buf[127] = '\0';
-		PR_Close(pds[j].fd);
-		printf("The server received \"%s\" from a client\n", buf);
-	    } else if (pds[j].out_flags & PR_POLL_ERR) {
-		fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
-		exit(1);
-	    } else if (pds[j].out_flags & PR_POLL_NVAL) {
-		fprintf(stderr, "PR_Poll() indicates that an fd is invalid\n");
-		exit(1);
-	    } else {
-		other_pds[nextIndex] = pds[j];
-		nextIndex++;
-	    }
-	}
 
-	PR_ASSERT(retVal == nEvents);
-	/* swap */
-	tmp = pds;
-	pds = other_pds;
-	other_pds = tmp;
-	npds = nextIndex;
-	i += nEvents;
+		PR_ASSERT(retVal == nEvents);
+		/* swap */
+		tmp = pds;
+		pds = other_pds;
+		other_pds = tmp;
+		npds = nextIndex;
+		i += nEvents;
     }
+    PR_DestroySocketPollFd(socket_poll_fd);
 
     printf("All tests finished\n");
     PR_Cleanup();
     return 0;
 }