Tcl Source Code

Artifact [7c209164d6]
Login

Artifact 7c209164d6c8941b7556bd450ec3da63098b08e5:

Attachment "tcl8.6.6.patch" to ticket [40eb6131cf] added by msaal 2017-03-09 18:02:40. (unpublished)
diff -Nuar tcl8.6.6.orig/unix/tclUnixChan.c tcl8.6.6/unix/tclUnixChan.c
--- tcl8.6.6.orig/unix/tclUnixChan.c	2016-02-25 15:12:38.000000000 -0500
+++ tcl8.6.6/unix/tclUnixChan.c	2017-03-01 13:09:36.545330731 -0500
@@ -48,6 +48,8 @@
 
 #endif	/* HAVE_TERMIOS_H */
 
+#include <poll.h>
+
 /*
  * Helper macros to make parts of this file clearer. The macros do exactly
  * what they say on the tin. :-) They also only ever refer to their arguments
@@ -1764,22 +1766,9 @@
 				 * forever. */
 {
     Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */
-    struct timeval blockTime, *timeoutPtr;
     int numFound, result = 0;
-    fd_set readableMask;
-    fd_set writableMask;
-    fd_set exceptionMask;
-
-#ifndef _DARWIN_C_SOURCE
-    /*
-     * Sanity check fd.
-     */
-
-    if (fd >= FD_SETSIZE) {
-	Tcl_Panic("TclUnixWaitForFile can't handle file id %d", fd);
-	/* must never get here, or select masks overrun will occur below */
-    }
-#endif
+    int msec = 0;
+    struct pollfd pollfd;
 
     /*
      * If there is a non-zero finite timeout, compute the time when we give
@@ -1794,70 +1783,56 @@
 	    abortTime.usec -= 1000000;
 	    abortTime.sec += 1;
 	}
-	timeoutPtr = &blockTime;
     } else if (timeout == 0) {
-	timeoutPtr = &blockTime;
-	blockTime.tv_sec = 0;
-	blockTime.tv_usec = 0;
+	msec = 0;
     } else {
-	timeoutPtr = NULL;
+	msec = -1;
     }
 
     /*
-     * Initialize the select masks.
-     */
-
-    FD_ZERO(&readableMask);
-    FD_ZERO(&writableMask);
-    FD_ZERO(&exceptionMask);
-
-    /*
      * Loop in a mini-event loop of our own, waiting for either the file to
      * become ready or a timeout to occur.
      */
 
     while (1) {
 	if (timeout > 0) {
-	    blockTime.tv_sec = abortTime.sec - now.sec;
-	    blockTime.tv_usec = abortTime.usec - now.usec;
-	    if (blockTime.tv_usec < 0) {
-		blockTime.tv_sec -= 1;
-		blockTime.tv_usec += 1000000;
-	    }
-	    if (blockTime.tv_sec < 0) {
-		blockTime.tv_sec = 0;
-		blockTime.tv_usec = 0;
+            msec  = (abortTime.sec - now.sec)*1000;
+            msec += (abortTime.usec - now.usec)/1000;
+	    if (msec < 0) {
+		msec = 0;
 	    }
 	}
 
 	/*
-	 * Setup the select masks for the fd.
+	 * Setup poll for the fd.
 	 */
 
+        pollfd.fd     = fd;
+        pollfd.events = 0;
 	if (mask & TCL_READABLE) {
-	    FD_SET(fd, &readableMask);
+	     pollfd.events |= POLLIN | POLLHUP;
 	}
 	if (mask & TCL_WRITABLE) {
-	    FD_SET(fd, &writableMask);
+	     pollfd.events |= POLLOUT;
 	}
 	if (mask & TCL_EXCEPTION) {
-	    FD_SET(fd, &exceptionMask);
+	     pollfd.events |= POLLERR;
 	}
 
 	/*
 	 * Wait for the event or a timeout.
 	 */
 
-	numFound = select(fd + 1, &readableMask, &writableMask,
-		&exceptionMask, timeoutPtr);
+	numFound = poll(&pollfd, 1, msec);
 	if (numFound == 1) {
-	    if (FD_ISSET(fd, &readableMask)) {
+	    pollfd.revents &= pollfd.events;
+	    if (pollfd.revents & (POLLIN | POLLHUP)) {
 		SET_BITS(result, TCL_READABLE);
 	    }
-	    if (FD_ISSET(fd, &writableMask)) {
+	    if (pollfd.revents & POLLOUT) {
 		SET_BITS(result, TCL_WRITABLE);
 	    }
-	    if (FD_ISSET(fd, &exceptionMask)) {
+	    if (pollfd.revents & POLLERR) {
 		SET_BITS(result, TCL_EXCEPTION);
 	    }
 	    result &= mask;
diff -Nuar tcl8.6.6.orig/unix/tclUnixNotfy.c tcl8.6.6/unix/tclUnixNotfy.c
--- tcl8.6.6.orig/unix/tclUnixNotfy.c	2016-03-23 07:43:13.000000000 -0400
+++ tcl8.6.6/unix/tclUnixNotfy.c	2017-03-01 13:12:43.580607804 -0500
@@ -16,6 +16,7 @@
 #include "tclInt.h"
 #ifndef HAVE_COREFOUNDATION	/* Darwin/Mac OS X CoreFoundation notifier is
 				 * in tclMacOSXNotify.c */
+#include <poll.h>
 #include <signal.h>
 
 /*
@@ -51,16 +52,8 @@
 				 * the event is queued). */
 } FileHandlerEvent;
 
-/*
- * The following structure contains a set of select() masks to track readable,
- * writable, and exception conditions.
- */
-
-typedef struct SelectMasks {
-    fd_set readable;
-    fd_set writable;
-    fd_set exception;
-} SelectMasks;
+#define POLLFD_SIZE	4096
+#define NELEM(a)	(sizeof (a)/sizeof *(a))
 
 /*
  * The following static structure contains the state information for the
@@ -71,17 +64,10 @@
 typedef struct ThreadSpecificData {
     FileHandler *firstFileHandlerPtr;
 				/* Pointer to head of file handler list. */
-    SelectMasks checkMasks;	/* This structure is used to build up the
-				 * masks to be used in the next call to
-				 * select. Bits are set in response to calls
-				 * to Tcl_CreateFileHandler. */
-    SelectMasks readyMasks;	/* This array reflects the readable/writable
-				 * conditions that were found to exist by the
-				 * last call to select. */
-    int numFdBits;		/* Number of valid bits in checkMasks (one
-				 * more than highest fd for which
-				 * Tcl_WatchFile has been called). */
+    int		  fds_size;
+    struct pollfd fds[POLLFD_SIZE];
 #ifdef TCL_THREADS
+    int		  fds_offset;	/* offset in notifier fds array */
     int onList;			/* True if it is in this list */
     unsigned int pollState;	/* pollState is used to implement a polling
 				 * handshake between each thread and the
@@ -110,6 +96,39 @@
 #endif /* TCL_THREADS */
 } ThreadSpecificData;
 
+static inline int
+fds_index(
+    ThreadSpecificData *tsdPtr,
+    int fd)
+{
+    int min = 0;
+    int max = tsdPtr->fds_size - 1;
+    int ii;
+
+    while (max >= min) {
+	ii = (max + min)/2;
+	if (tsdPtr->fds[ii].fd == fd) {
+	    return ii;
+	}
+	if (fd > tsdPtr->fds[ii].fd) {
+	    min = ii + 1;
+	} else {
+	    max = ii - 1;
+	}
+    }
+
+    return -1;
+}
+
+static inline struct pollfd *
+fds_pointer(
+    ThreadSpecificData *tsdPtr,
+    int fd)
+{
+    int ii = fds_index(tsdPtr, fd);
+    return ii != -1 ? &tsdPtr->fds[ii] : NULL;
+}
+
 static Tcl_ThreadDataKey dataKey;
 
 #ifdef TCL_THREADS
@@ -153,7 +172,7 @@
  */
 
 pthread_mutex_t notifierInitMutex = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t notifierMutex     = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t notifierMutex	  = PTHREAD_MUTEX_INITIALIZER;
 /*
  * The following static indicates if the notifier thread is running.
  *
@@ -608,6 +627,8 @@
     } else {
 	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
 	FileHandler *filePtr;
+	int ii;
+	struct pollfd *fdsp;
 
 	for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
 		filePtr = filePtr->nextPtr) {
@@ -630,23 +651,32 @@
 	 * Update the check masks for this file.
 	 */
 
+	fdsp = fds_pointer(tsdPtr, fd);
+	if (fdsp == NULL && tsdPtr->fds_size < NELEM(tsdPtr->fds)) {
+	    for (ii = 0; ii < tsdPtr->fds_size; ii++) {
+		if (tsdPtr->fds[ii].fd > fd) {
+		    break;
+		}
+	    }
+	    fdsp = &tsdPtr->fds[ii];
+	    memmove(&tsdPtr->fds[ii+1], &tsdPtr->fds[ii],
+		    sizeof tsdPtr->fds[ii] * (tsdPtr->fds_size - ii));
+	    tsdPtr->fds_size++;
+	    fdsp->fd = fd;
+	}
+	if (fdsp == NULL) {
+	    Tcl_Panic("%s: out of file descriptor space", __FUNCTION__);
+	}
+
+	fdsp->events = 0;
 	if (mask & TCL_READABLE) {
-	    FD_SET(fd, &tsdPtr->checkMasks.readable);
-	} else {
-	    FD_CLR(fd, &tsdPtr->checkMasks.readable);
+	    fdsp->events |= POLLIN | POLLHUP;
 	}
 	if (mask & TCL_WRITABLE) {
-	    FD_SET(fd, &tsdPtr->checkMasks.writable);
-	} else {
-	    FD_CLR(fd, &tsdPtr->checkMasks.writable);
+	    fdsp->events |= POLLOUT;
 	}
 	if (mask & TCL_EXCEPTION) {
-	    FD_SET(fd, &tsdPtr->checkMasks.exception);
-	} else {
-	    FD_CLR(fd, &tsdPtr->checkMasks.exception);
-	}
-	if (tsdPtr->numFdBits <= fd) {
-	    tsdPtr->numFdBits = fd+1;
+	    fdsp->events |= POLLERR;
 	}
     }
 }
@@ -677,7 +707,7 @@
 	return;
     } else {
 	FileHandler *filePtr, *prevPtr;
-	int i;
+	int ii;
 	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
 
 	/*
@@ -698,32 +728,11 @@
 	 * Update the check masks for this file.
 	 */
 
-	if (filePtr->mask & TCL_READABLE) {
-	    FD_CLR(fd, &tsdPtr->checkMasks.readable);
-	}
-	if (filePtr->mask & TCL_WRITABLE) {
-	    FD_CLR(fd, &tsdPtr->checkMasks.writable);
-	}
-	if (filePtr->mask & TCL_EXCEPTION) {
-	    FD_CLR(fd, &tsdPtr->checkMasks.exception);
-	}
-
-	/*
-	 * Find current max fd.
-	 */
-
-	if (fd+1 == tsdPtr->numFdBits) {
-	    int numFdBits = 0;
-
-	    for (i = fd-1; i >= 0; i--) {
-		if (FD_ISSET(i, &tsdPtr->checkMasks.readable)
-			|| FD_ISSET(i, &tsdPtr->checkMasks.writable)
-			|| FD_ISSET(i, &tsdPtr->checkMasks.exception)) {
-		    numFdBits = i+1;
-		    break;
-		}
-	    }
-	    tsdPtr->numFdBits = numFdBits;
+	ii = fds_index(tsdPtr, fd);
+	if (ii != -1) {
+	    tsdPtr->fds_size--;
+	    memmove(&tsdPtr->fds[ii], &tsdPtr->fds[ii+1],
+		    sizeof tsdPtr->fds[ii] * (tsdPtr->fds_size - ii));
 	}
 
 	/*
@@ -864,7 +873,7 @@
 	return tclNotifierHooks.waitForEventProc(timePtr);
     } else {
 	FileHandler *filePtr;
-	int mask;
+	int mask, ii;
 	Tcl_Time vTime;
 #ifdef TCL_THREADS
 	int waitForFiles;
@@ -872,13 +881,6 @@
 	MSG msg;
 #   endif /* __CYGWIN__ */
 #else
-	/*
-	 * Impl. notes: timeout & timeoutPtr are used if, and only if threads
-	 * are not enabled. They are the arguments for the regular select()
-	 * used when the core is not thread-enabled.
-	 */
-
-	struct timeval timeout, *timeoutPtr;
 	int numFound;
 #endif /* TCL_THREADS */
 	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
@@ -902,10 +904,7 @@
 		timePtr = &vTime;
 	    }
 #ifndef TCL_THREADS
-	    timeout.tv_sec = timePtr->sec;
-	    timeout.tv_usec = timePtr->usec;
-	    timeoutPtr = &timeout;
-	} else if (tsdPtr->numFdBits == 0) {
+	} else if (tsdPtr->fds_size == 0) {
 	    /*
 	     * If there are no threads, no timeout, and no fds registered,
 	     * then there are no events possible and we must avoid deadlock.
@@ -915,8 +914,6 @@
 	     */
 
 	    return -1;
-	} else {
-	    timeoutPtr = NULL;
 #endif /* !TCL_THREADS */
 	}
 
@@ -954,10 +951,16 @@
 	    tsdPtr->pollState = POLL_WANT;
 	    timePtr = NULL;
 	} else {
-	    waitForFiles = (tsdPtr->numFdBits > 0);
+	    waitForFiles = (tsdPtr->fds_size > 0);
 	    tsdPtr->pollState = 0;
 	}
 
+	/* initialize events */
+	tsdPtr->fds_offset = -1;
+	for (ii = 0; ii < tsdPtr->fds_size; ii++) {
+	    tsdPtr->fds[ii].revents = 0;
+	}
+
 	if (waitForFiles) {
 	    /*
 	     * Add the ThreadSpecificData structure of this thread to the list
@@ -979,10 +982,6 @@
 	    }
 	}
 
-	FD_ZERO(&tsdPtr->readyMasks.readable);
-	FD_ZERO(&tsdPtr->readyMasks.writable);
-	FD_ZERO(&tsdPtr->readyMasks.exception);
-
 	if (!tsdPtr->eventReady) {
 #ifdef __CYGWIN__
 	    if (!PeekMessageW(&msg, NULL, 0, 0, 0)) {
@@ -1058,10 +1057,8 @@
 	}
 
 #else
-	tsdPtr->readyMasks = tsdPtr->checkMasks;
-	numFound = select(tsdPtr->numFdBits, &tsdPtr->readyMasks.readable,
-		&tsdPtr->readyMasks.writable, &tsdPtr->readyMasks.exception,
-		timeoutPtr);
+	numFound = poll(tsdPtr->fds, tsdPtr->fds_size, timePtr == NULL
+			? -1 : (timePtr->sec*1000)+(timePtr->usec/1000));
 
 	/*
 	 * Some systems don't clear the masks after an error, so we have to do
@@ -1069,9 +1066,9 @@
 	 */
 
 	if (numFound == -1) {
-	    FD_ZERO(&tsdPtr->readyMasks.readable);
-	    FD_ZERO(&tsdPtr->readyMasks.writable);
-	    FD_ZERO(&tsdPtr->readyMasks.exception);
+	    for (ii = 0; ii < tsdPtr->fds_size; ii++) {
+		tsdPtr->fds[ii].revents = 0;
+	    }
 	}
 #endif /* TCL_THREADS */
 
@@ -1081,14 +1078,28 @@
 
 	for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
 		filePtr = filePtr->nextPtr) {
+	    struct pollfd *fdsp = fds_pointer(tsdPtr, filePtr->fd);
+	    short revents;
+
+	    if (fdsp == NULL) {
+		Tcl_Panic("%s: fd=%d not found", __FUNCTION__, filePtr->fd);
+		continue;
+	    }
+
+	    if (fdsp->revents & POLLNVAL) {
+		/* file descriptor is no longer valid, skip it */
+		continue;
+	    }
+
 	    mask = 0;
-	    if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.readable)) {
+	    revents = fdsp->revents & fdsp->events;
+	    if (revents & (POLLIN | POLLHUP)) {
 		mask |= TCL_READABLE;
 	    }
-	    if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.writable)) {
+	    if (revents & POLLOUT) {
 		mask |= TCL_WRITABLE;
 	    }
-	    if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.exception)) {
+	    if (revents & POLLERR) {
 		mask |= TCL_EXCEPTION;
 	    }
 
@@ -1148,13 +1159,12 @@
     ClientData clientData)	/* Not used. */
 {
     ThreadSpecificData *tsdPtr;
-    fd_set readableMask;
-    fd_set writableMask;
-    fd_set exceptionMask;
+    struct pollfd * fdsp;
+    int fds_size, fds_num;
     int fds[2];
-    int i, numFdBits = 0, receivePipe;
+    int ii, receivePipe;
     long found;
-    struct timeval poll = {0., 0.}, *timePtr;
+    int timeout;
     char buf[2];
 
     if (pipe(fds) != 0) {
@@ -1195,59 +1205,70 @@
     pthread_mutex_unlock(&notifierMutex);
 
     /*
+     * Allocate initial poll mask.
+     */
+
+    fds_size = POLLFD_SIZE;
+    fdsp     = ckalloc((sizeof *fdsp) * fds_size);
+
+    /*
      * Look for file events and report them to interested threads.
      */
 
     while (1) {
-	FD_ZERO(&readableMask);
-	FD_ZERO(&writableMask);
-	FD_ZERO(&exceptionMask);
-
 	/*
 	 * Compute the logical OR of the select masks from all the waiting
 	 * notifiers.
 	 */
 
+	timeout = -1;
+
 	pthread_mutex_lock(&notifierMutex);
-	timePtr = NULL;
+
+	/*
+	 * Make sure we haved enough space in the poll mask for receive pipe
+	 * and file descriptors.
+	 */
+
+	fds_num = 1;
 	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
-	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
-		if (FD_ISSET(i, &tsdPtr->checkMasks.readable)) {
-		    FD_SET(i, &readableMask);
-		}
-		if (FD_ISSET(i, &tsdPtr->checkMasks.writable)) {
-		    FD_SET(i, &writableMask);
-		}
-		if (FD_ISSET(i, &tsdPtr->checkMasks.exception)) {
-		    FD_SET(i, &exceptionMask);
-		}
+	    fds_num += tsdPtr->fds_size;
+	}
+	if (fds_num > fds_size) {
+	    while (fds_num > fds_size) {
+		fds_size += POLLFD_SIZE;
 	    }
-	    if (tsdPtr->numFdBits > numFdBits) {
-		numFdBits = tsdPtr->numFdBits;
+	    fdsp = ckrealloc(fdsp, (sizeof *fdsp) * fds_size);
+	}
+
+	/*
+	 * Add receive pipe and file descriptors to poll mask.
+	 */
+
+	fdsp[0].fd     = receivePipe;
+	fdsp[0].events = POLLIN | POLLHUP;
+	fds_num = 1;
+	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
+	    tsdPtr->fds_offset = fds_num;
+	    for (ii = 0; ii < tsdPtr->fds_size; ii++) {
+		fdsp[fds_num].fd     = tsdPtr->fds[ii].fd;
+		fdsp[fds_num].events = tsdPtr->fds[ii].events;
+		fds_num++;
 	    }
 	    if (tsdPtr->pollState & POLL_WANT) {
 		/*
-		 * Here we make sure we go through select() with the same mask
+		 * Here we make sure we go through poll() with the same mask
 		 * bits that were present when the thread tried to poll.
 		 */
 
 		tsdPtr->pollState |= POLL_DONE;
-		timePtr = &poll;
+		timeout = 0;
 	    }
 	}
-	pthread_mutex_unlock(&notifierMutex);
-
-	/*
-	 * Set up the select mask to include the receive pipe.
-	 */
 
-	if (receivePipe >= numFdBits) {
-	    numFdBits = receivePipe + 1;
-	}
-	FD_SET(receivePipe, &readableMask);
+	pthread_mutex_unlock(&notifierMutex);
 
-	if (select(numFdBits, &readableMask, &writableMask, &exceptionMask,
-		timePtr) == -1) {
+	if (poll(fdsp, fds_num, timeout) == -1) {
 	    /*
 	     * Try again immediately on an error.
 	     */
@@ -1261,22 +1282,14 @@
 
 	pthread_mutex_lock(&notifierMutex);
 	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
-	    found = 0;
+	    if (tsdPtr->fds_offset < 0) {
+		continue;
+	    }
 
-	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
-		if (FD_ISSET(i, &tsdPtr->checkMasks.readable)
-			&& FD_ISSET(i, &readableMask)) {
-		    FD_SET(i, &tsdPtr->readyMasks.readable);
-		    found = 1;
-		}
-		if (FD_ISSET(i, &tsdPtr->checkMasks.writable)
-			&& FD_ISSET(i, &writableMask)) {
-		    FD_SET(i, &tsdPtr->readyMasks.writable);
-		    found = 1;
-		}
-		if (FD_ISSET(i, &tsdPtr->checkMasks.exception)
-			&& FD_ISSET(i, &exceptionMask)) {
-		    FD_SET(i, &tsdPtr->readyMasks.exception);
+	    found = 0;
+	    for (ii = 0; ii < tsdPtr->fds_size; ii++) {
+		tsdPtr->fds[ii].revents = fdsp[tsdPtr->fds_offset + ii].revents;
+		if (tsdPtr->fds[ii].revents & tsdPtr->fds[ii].events) {
 		    found = 1;
 		}
 	    }
@@ -1318,10 +1331,10 @@
 	 * avoid a race condition we only read one at a time.
 	 */
 
-	if (FD_ISSET(receivePipe, &readableMask)) {
-	    i = read(receivePipe, buf, 1);
+	if (fdsp[0].revents & (POLLIN | POLLHUP)) {
+	    ii = read(receivePipe, buf, 1);
 
-	    if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
+	    if ((ii == 0) || ((ii == 1) && (buf[0] == 'q'))) {
 		/*
 		 * Someone closed the write end of the pipe or sent us a Quit
 		 * message [Bug: 4139] and then closed the write end of the
@@ -1343,6 +1356,7 @@
     triggerPipe = -1;
     pthread_cond_broadcast(&notifierCV);
     pthread_mutex_unlock(&notifierMutex);
+    ckfree(fdsp);
 
     TclpThreadExit(0);
 }