clone 571423 -1
reassign -1 perl-tk 1:804.028-6
retitle -1 perl-tk: broken select mask on 64 bit architectures
severity -1 important
tags -1 patch fixed-upstream
block 571423 by -1
submitter -1 !
thanks

On Wed, Mar 17, 2010 at 07:34:44PM +0100, gregor herrmann wrote:
> On Wed, 17 Mar 2010 17:12:39 +0100, Jozef Kutej wrote:
> 
> > the tests failed with:
> > perl-tk/squeeze uptodate 1:804.028-6
> > but after I've installed by hand:
> > http://search.cpan.org/CPAN/authors/id/S/SR/SREZIC/Tk-804.028_502.tar.gz
> > all tests passed.
> 
> Now that's interesting, thanks for your analysis!
> 
> I'm CC'ing the perl-tk maintainer to get his attention for this
> issue.

This is also reported as [rt.cpan.org #32034]. I'm attaching
the relevant patch:

URL: https://svn.perl.org/modules/Tk/trunk

r10469 | srezic | 2008-01-02 01:01:50 +0200 (Wed, 02 Jan 2008) | 6 lines

 r1...@biokovo-amd64:  eserte | 2008-01-01 22:47:35 +0100
 * manually applied the tcl repo diff between 1.11.2.4 and 1.11.2.5 
   This seems to solve the POE problem reported in
   http://rt.cpan.org/Ticket/Display.html?id=32034
 * new fileevent2.t test for checking this problem

I've verified that this fixes libpoe-loop-tk-perl for me on amd64.
Cloning, reassigning and blocking accordingly.
-- 
Niko Tyni   nt...@debian.org
URL: https://svn.perl.org/modules/Tk/trunk

r10469 | srezic | 2008-01-02 01:01:50 +0200 (Wed, 02 Jan 2008) | 6 lines

 r1...@biokovo-amd64:  eserte | 2008-01-01 22:47:35 +0100
 * manually applied the tcl repo diff between 1.11.2.4 and 1.11.2.5 
   This seems to solve the POE problem reported in
   http://rt.cpan.org/Ticket/Display.html?id=32034
 * new fileevent2.t test for checking this problem

Index: t/fileevent2.t
===================================================================
--- t/fileevent2.t	(revision 0)
+++ t/fileevent2.t	(revision 10469)
@@ -0,0 +1,49 @@
+#!/usr/bin/perl -w
+# -*- perl -*-
+
+#
+# $Id: $
+# Author: Slaven Rezic
+#
+
+use strict;
+
+use Tk;
+
+BEGIN {
+    if (!eval q{
+	use 5.006; # three-arg open
+	use Test::More;
+	1;
+    }) {
+	print "1..0 # skip: no Test::More module\n";
+	exit;
+    }
+}
+
+plan tests => 1;
+
+my @fh;
+my $callback_called = 0;
+
+my $mw = tkinit;
+$mw->geometry("+10+10");
+$mw->idletasks;
+
+# A variant of the problem reported in
+# http://rt.cpan.org/Ticket/Display.html?id=32034
+#
+# tclUnixNotify.c used to do bit-handling for the select() mask
+# itself, but this was broken for 64bit machines.
+for (1..100) {
+    open my $dup, "<&", \*STDIN or die "Can't dup STDIN: $!";
+    push @fh, $dup;
+    $mw->fileevent($dup, "readable", sub { $callback_called++ });
+}
+
+$mw->after(300, sub { $mw->destroy });
+MainLoop;
+
+ok($callback_called == 0, "Fileevent callback should never be called");
+
+__END__

Property changes on: t/fileevent2.t
___________________________________________________________________
Added: svn:executable
   + *

Index: MANIFEST
===================================================================
--- MANIFEST	(revision 10468)
+++ MANIFEST	(revision 10469)
@@ -1962,6 +1962,7 @@
 t/exefiles.t
 t/fbox.t
 t/fileevent.t
+t/fileevent2.t
 t/fileselect.t
 t/font.t
 t/fork.t
Index: pTk/mTk/tclUnix/tclUnixNotfy.c
===================================================================
--- pTk/mTk/tclUnix/tclUnixNotfy.c	(revision 10468)
+++ pTk/mTk/tclUnix/tclUnixNotfy.c	(revision 10469)
@@ -69,6 +69,18 @@
 } FileHandlerEvent;
 
 /*
+ *
+ * The following structure contains a set of select() masks to track
+ * readable, writable, and exceptional conditions.
+ */
+
+typedef struct SelectMasks {
+    fd_set readable;
+    fd_set writable;
+    fd_set exceptional;
+} SelectMasks;
+
+/*
  * The following static structure contains the state information for the
  * select based implementation of the Tcl notifier.  One of these structures
  * is created for each thread that is using the notifier.
@@ -77,13 +89,12 @@
 typedef struct ThreadSpecificData {
     FileHandler *firstFileHandlerPtr;
 				/* Pointer to head of file handler list. */
-    fd_mask checkMasks[3*MASK_SIZE];
-				/* This array is used to build up the masks
+    
+    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. */
-    fd_mask readyMasks[3*MASK_SIZE];
-				/* This array reflects the readable/writable
+    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
@@ -425,7 +436,6 @@
 {
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
     FileHandler *filePtr;
-    int index, bit;
 
     if (tclStubs.tcl_CreateFileHandler != tclOriginalNotifier.createFileHandlerProc) {
 	tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
@@ -453,22 +463,20 @@
      * Update the check masks for this file.
      */
 
-    index = fd/(NBBY*sizeof(fd_mask));
-    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
-    if (mask & TCL_READABLE) {
-	tsdPtr->checkMasks[index] |= bit;
+    if ( mask & TCL_READABLE ) {
+	FD_SET( fd, &(tsdPtr->checkMasks.readable) );
     } else {
-	tsdPtr->checkMasks[index] &= ~bit;
+	FD_CLR( fd, &(tsdPtr->checkMasks.readable) );
     }
-    if (mask & TCL_WRITABLE) {
-	(tsdPtr->checkMasks+MASK_SIZE)[index] |= bit;
+    if ( mask & TCL_WRITABLE ) {
+	FD_SET( fd, &(tsdPtr->checkMasks.writable) );
     } else {
-	(tsdPtr->checkMasks+MASK_SIZE)[index] &= ~bit;
+	FD_CLR( fd, &(tsdPtr->checkMasks.writable) );
     }
-    if (mask & TCL_EXCEPTION) {
-	(tsdPtr->checkMasks+2*(MASK_SIZE))[index] |= bit;
+    if ( mask & TCL_EXCEPTION ) {
+	FD_SET( fd, &(tsdPtr->checkMasks.exceptional) );
     } else {
-	(tsdPtr->checkMasks+2*(MASK_SIZE))[index] &= ~bit;
+	FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) );
     }
     if (tsdPtr->numFdBits <= fd) {
 	tsdPtr->numFdBits = fd+1;
@@ -497,8 +505,7 @@
     int fd;             /* Stream id for which to remove callback procedure. */
 {
     FileHandler *filePtr, *prevPtr;
-    int index, bit, i;
-    unsigned long flags;
+    int i;
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
 
     if (tclStubs.tcl_DeleteFileHandler != tclOriginalNotifier.deleteFileHandlerProc) {
@@ -524,17 +531,14 @@
      * Update the check masks for this file.
      */
 
-    index = fd/(NBBY*sizeof(fd_mask));
-    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
-
     if (filePtr->mask & TCL_READABLE) {
-	tsdPtr->checkMasks[index] &= ~bit;
+	FD_CLR( fd, &(tsdPtr->checkMasks.readable) );
     }
     if (filePtr->mask & TCL_WRITABLE) {
-	(tsdPtr->checkMasks+MASK_SIZE)[index] &= ~bit;
+	FD_CLR( fd, &(tsdPtr->checkMasks.writable) );
     }
     if (filePtr->mask & TCL_EXCEPTION) {
-	(tsdPtr->checkMasks+2*(MASK_SIZE))[index] &= ~bit;
+	FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) );
     }
 
     /*
@@ -542,17 +546,12 @@
      */
 
     if (fd+1 == tsdPtr->numFdBits) {
-	for (tsdPtr->numFdBits = 0; index >= 0; index--) {
-	    flags = tsdPtr->checkMasks[index]
-		| (tsdPtr->checkMasks+MASK_SIZE)[index]
-		| (tsdPtr->checkMasks+2*(MASK_SIZE))[index];
-	    if (flags) {
-		for (i = (NBBY*sizeof(fd_mask)); i > 0; i--) {
-		    if (flags & (((unsigned long)1) << (i-1))) {
-			break;
-		    }
-		}
-		tsdPtr->numFdBits = index * (NBBY*sizeof(fd_mask)) + i;
+	tsdPtr->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.exceptional ) ) ) {
+		tsdPtr->numFdBits = i+1;
 		break;
 	    }
 	}
@@ -674,7 +673,7 @@
     FileHandler *filePtr;
     FileHandlerEvent *fileEvPtr;
     struct timeval timeout, *timeoutPtr;
-    int bit, index, mask;
+    int mask;
 #ifdef TCL_THREADS
     int waitForFiles;
 #else
@@ -756,7 +755,9 @@
 	write(triggerPipe, "", 1);
     }
 
-    memset((VOID *) tsdPtr->readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
+    FD_ZERO( &(tsdPtr->readyMasks.readable) );
+    FD_ZERO( &(tsdPtr->readyMasks.writable) );
+    FD_ZERO( &(tsdPtr->readyMasks.exceptional) );
 
     if (!tsdPtr->eventReady) {
 	Tcl_ConditionWait(&tsdPtr->waitCV, &notifierMutex, timePtr);
@@ -786,12 +787,12 @@
 
 
 #else
-    memcpy((VOID *) tsdPtr->readyMasks, (VOID *) tsdPtr->checkMasks,
-	    3*MASK_SIZE*sizeof(fd_mask));
-    numFound = select(tsdPtr->numFdBits,
-	    (SELECT_MASK *) &tsdPtr->readyMasks[0],
-	    (SELECT_MASK *) &tsdPtr->readyMasks[MASK_SIZE],
-	    (SELECT_MASK *) &tsdPtr->readyMasks[2*MASK_SIZE], timeoutPtr);
+    tsdPtr->readyMasks = tsdPtr->checkMasks;
+    numFound = select( tsdPtr->numFdBits,
+		       &(tsdPtr->readyMasks.readable),
+		       &(tsdPtr->readyMasks.writable),
+		       &(tsdPtr->readyMasks.exceptional),
+		       timeoutPtr );
 
     /*
      * Some systems don't clear the masks after an error, so
@@ -799,7 +800,9 @@
      */
 
     if (numFound == -1) {
-	memset((VOID *) tsdPtr->readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
+	FD_ZERO( &(tsdPtr->readyMasks.readable ) );
+	FD_ZERO( &(tsdPtr->readyMasks.writable ) );
+	FD_ZERO( &(tsdPtr->readyMasks.exceptional ) );
     }
 
 #ifdef _LANG
@@ -819,17 +822,15 @@
 
     for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
 	 filePtr = filePtr->nextPtr) {
-	index = filePtr->fd / (NBBY*sizeof(fd_mask));
-	bit = 1 << (filePtr->fd % (NBBY*sizeof(fd_mask)));
+
 	mask = 0;
-
-	if (tsdPtr->readyMasks[index] & bit) {
+	if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.readable) ) ) {
 	    mask |= TCL_READABLE;
 	}
-	if ((tsdPtr->readyMasks+MASK_SIZE)[index] & bit) {
+	if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.writable) ) ) {
 	    mask |= TCL_WRITABLE;
 	}
-	if ((tsdPtr->readyMasks+2*(MASK_SIZE))[index] & bit) {
+	if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.exceptional) ) ) {
 	    mask |= TCL_EXCEPTION;
 	}
 
@@ -888,13 +889,13 @@
     ClientData clientData;      /* Not used. */
 {
     ThreadSpecificData *tsdPtr;
-    fd_mask masks[3*MASK_SIZE];
-    long *maskPtr = (long *)masks;      /* masks[] cast to type long[] */
+    fd_set readableMask;
+    fd_set writableMask;
+    fd_set exceptionalMask;
     int fds[2];
-    int i, status, index, bit, numFdBits, receivePipe;
-    long found, word;
+    int i, status, numFdBits, receivePipe;
+    long found;
     struct timeval poll = {0., 0.}, *timePtr;
-    int maskSize = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask);
     char buf[2];
 
     if (pipe(fds) != 0) {
@@ -942,27 +943,31 @@
      */
 
     while (1) {
-	/*
-	 * Set up the select mask to include the receive pipe.
-	 */
 
-	memset((VOID *)masks, 0, 3*MASK_SIZE*sizeof(fd_mask));
-	numFdBits = receivePipe + 1;
-	index = receivePipe / (NBBY*sizeof(fd_mask));
-	bit = 1 << (receivePipe % (NBBY*sizeof(fd_mask)));
-	masks[index] |= bit;
+	FD_ZERO( &readableMask );
+	FD_ZERO( &writableMask );
+	FD_ZERO( &exceptionalMask );
 
 	/*
-	 * Add in the check masks from all of the waiting notifiers.
+	 * Compute the logical OR of the select masks from all the
+	 * waiting notifiers.
 	 */
 
 	Tcl_MutexLock(&notifierMutex);
 	timePtr = NULL;
-	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
-	    for (i = 0; i < maskSize; i++) {
-		maskPtr[i] |= ((long*)tsdPtr->checkMasks)[i];
+        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.exceptional) ) ) {
+		    FD_SET( i, &exceptionalMask );
+		}
 	    }
-	    if (tsdPtr->numFdBits > numFdBits) {
+	    if ( tsdPtr->numFdBits > numFdBits ) {
 		numFdBits = tsdPtr->numFdBits;
 	    }
 	    if (tsdPtr->pollState & POLL_WANT) {
@@ -977,11 +982,17 @@
 	}
 	Tcl_MutexUnlock(&notifierMutex);
 
-	maskSize = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask);
+	/*
+	 * Set up the select mask to include the receive pipe.
+	 */
 
-	if (select(numFdBits, (SELECT_MASK *) &masks[0],
-		(SELECT_MASK *) &masks[MASK_SIZE],
-		(SELECT_MASK *) &masks[2*MASK_SIZE], timePtr) == -1) {
+	if ( receivePipe >= numFdBits ) {
+	    numFdBits = receivePipe + 1;
+	}
+	FD_SET( receivePipe, &readableMask );
+
+	if ( select( numFdBits, &readableMask, &writableMask,
+		     &exceptionalMask, timePtr) == -1 ) {
 	    /*
 	     * Try again immediately on an error.
 	     */
@@ -997,11 +1008,24 @@
 	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
 	    found = 0;
 
-	    for (i = 0; i < maskSize; i++) {
-		word = maskPtr[i] & ((long*)tsdPtr->checkMasks)[i];
-		found |= word;
-		(((long*)(tsdPtr->readyMasks))[i]) = word;
+	    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.exceptional) )
+		     && FD_ISSET( i, &exceptionalMask ) ) {
+		    FD_SET( i, &(tsdPtr->readyMasks.exceptional) );
+		    found = 1;
+		}
 	    }
+			       
 	    if (found || (tsdPtr->pollState & POLL_DONE)) {
 		tsdPtr->eventReady = 1;
 		if (tsdPtr->onList) {
@@ -1035,7 +1059,7 @@
 	 * to avoid a race condition we only read one at a time.
 	 */
 
-	if (masks[index] & bit) {
+	if ( FD_ISSET( receivePipe, &readableMask ) ) {
 	    i = read(receivePipe, buf, 1);
 
 	    if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {

Property changes on: .
___________________________________________________________________
Modified: svk:merge
   - 8c7cb07b-dc62-db11-ac52-0211d89195f5:/local/Tk:1481
8c7cb07b-dc62-db11-ac52-0211d89195f5:/local/Tk-at-perl/trunk:1991
   + 8c7cb07b-dc62-db11-ac52-0211d89195f5:/local/Tk:1481
8c7cb07b-dc62-db11-ac52-0211d89195f5:/local/Tk-at-perl/trunk:1993

Reply via email to