scott@szilard.cray.COM (Scott Bolte) (01/26/89)
I have seen what appears to be a bug in X11R3 that occurs when using XtAddInput(). The routine that I pass in to be used to read input is called when there is no data. This results in a blocking read. This problem was reported officially a few weeks ago by our local X-pert. There has been no response yet so I tracked down the problem myself this today. A description, fix, and test case follow... The problem lies in the logic of XtAppNextEvent(). Due to the way in which it calls _XtwaitForSomething() and DoOtherSources() it is possible for two duplicate InputEvent entries to be added to the outstandingQueue. A blow by blow description is included as a comment in the workaround that follows. First a disclaimer: The code that follows fixes the symptom of the error instead of the actual problem. If I am correct a proper fix should (eventually :-) come from the X consortium. Given their workload it might be some time. In the meantime, if you too are having problems, apply the fix. Due to the informal nature of this posting I suggest you look at my logic before you make use of the fix. (After all, this is the first time that I looked at the NextEvent code.) What I do is scan the entire outstandingQueue before I add an InputEvent. If there is already an entry on the queue for the same source I do not do the insertion. Admittedly this will slow down processing the most at the worst time, when there are a lot of events queued. It is the most non-intrusive solution that I could come up with though. If anyone sees either a problem with this scheme or a better solution please post it as soon as possible. Scott Bolte Cray Research, Inc. scott@cray.com The "diff -c" listing of the old and new lib/Xt/NextEvent.c files..... *** /usr/galileo1/X11R3/lib/Xt/NextEvent.c Fri Oct 21 12:23:48 1988 --- NextEvent.c Wed Jan 25 17:35:39 1989 *************** *** 214,236 **** } } ! app->selectRqueue[i]->ie_oq = app->outstandingQueue; ! app->outstandingQueue = app->selectRqueue[i]; nfound--; } if (FD_ISSET (i, &wmaskfd)) { ! app->selectWqueue[i]->ie_oq = app->outstandingQueue; ! app->outstandingQueue = app->selectWqueue[i]; nfound--; } if (FD_ISSET (i, &emaskfd)) { ! app->selectEqueue[i]->ie_oq = app->outstandingQueue; ! app->outstandingQueue = app->selectEqueue[i]; nfound--; } ENDILOOP: ; } return ret; } static void IeCallProc(ptr) --- 214,291 ---- } } ! if( UniqueEntry(app->outstandingQueue,app->selectRqueue[i])){ ! app->selectRqueue[i]->ie_oq = app->outstandingQueue; ! app->outstandingQueue = app->selectRqueue[i]; ! } nfound--; } if (FD_ISSET (i, &wmaskfd)) { ! if( UniqueEntry(app->outstandingQueue,app->selectWqueue[i])){ ! app->selectWqueue[i]->ie_oq = app->outstandingQueue; ! app->outstandingQueue = app->selectWqueue[i]; ! } nfound--; } if (FD_ISSET (i, &emaskfd)) { ! if( UniqueEntry(app->outstandingQueue,app->selectEqueue[i])){ ! app->selectEqueue[i]->ie_oq = app->outstandingQueue; ! app->outstandingQueue = app->selectEqueue[i]; ! } nfound--; } ENDILOOP: ; } return ret; + } + + /* + * Insure that there are not any entries in the "ie_queue" that match + * the "ie_ptr". If there are return 0 otherwise return 1. + * + * This routine is a workaround that fixes a symptom rather than + * the incorrect logic of the input handler. It prevents more than + * one entry for any single source. + * + * The problem is in XtAppNextEvent(). What would happen is... + * + * 1) XtAppNextEvent() calls _XtwaitForSomething() directly. + * + * 2) data would come in on file descriptor FRED while + * _XtwaitForSomething() is blocked in select. + * + * 3) _XtwaitForSomething() will add the corresponding + * app->selectRqueue[FRED] to the outstandingQueue. + * + * 4) control returns back up to XtAppNextEvent(). + * + * 5) DoOtherSources() is called which in turns calls _XtwaitForSomething(). + * + * 6) _XtwaitForSomething() sees there is still data on FRED and adds + * a second selectRqueue[FRED] entry. + * + * 7) DoOtherSources() now calls IeCallProc() which results in the data + * being read off FRED. + * + * 8) one InputEvent is removed from the outstandingQueue. + * + * 9) DoOtherSources() sees the second entry on the outstandingQueue and + * calls IeCallProc() again. Unfortunately there no longer is any data to + * be read from FRED. + * + * 10) Twiddle(left_thumb,right_thumb) 'til data comes in. + * + */ + static int UniqueEntry(ie_ptr,ie_queue) + InputEvent *ie_ptr,*ie_queue; + { + register InputEvent *ptr; + + for( ptr = ie_queue ; ptr != (InputEvent *) 0 ; ptr = ptr->ie_oq ){ + if( ptr == ie_ptr ) + return(0); + } + return(1); } static void IeCallProc(ptr) ========================================================================= = = = End of diff, start of test case. = = = ========================================================================= #include <stdio.h> #include <X11/StringDefs.h> #include <X11/Xatom.h> #include <X11/Xlib.h> #include <X11/Intrinsic.h> main(argc, argv) int argc; char *argv[]; { int pipes[2], read_fd(); void time_out(); pipe(pipes); fprintf(stderr, "read pipe is %d and write pipe is %d\n", pipes[0], pipes[1]); (void) XtInitialize("master", "crayperf", NULL, 0, &argc, argv); (void) XtAddTimeOut(5000, time_out, pipes[1]); (void) XtAddInput(pipes[0], XtInputReadMask, read_fd, NULL); XtMainLoop(); } void time_out(fd, id) int fd; XtIntervalId *id; { int i; fprintf(stderr, "time_out() was called with fd %d.\n", fd); i = write(fd, "X", 1); fprintf(stderr, "time_out() wrote %d %s to fd %d\n", i ,i > 1 ? "bytes" : "byte", fd); XtAddTimeOut(5000, time_out, fd); } read_fd(junk, socket, InId) caddr_t junk; /* Client data for X callbacks */ int *socket; caddr_t InId; /* XtInputId if we were to use it */ { long t; char c; int i; t = time(0); fprintf(stderr, "read_fd() called at %ld.\n", t); if (read_check(*socket) <= 0) { fprintf(stderr, " ****** I do not agree that there is data.\n"); return; } i = read(*socket, &c, 1); switch (i) { case -1: perror("read returned an error. "); break; case 0: fprintf(stderr, "read returned zero bytes.\n"); break; case 1: fprintf(stderr, "read returned the '%c' character.\n", c); break; default: fprintf(stderr, "read returned more than a single character!\n"); break; } } read_check(fd) int fd; { int status; int rmask; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; rmask = (1 << fd); status = select(fd + 1, &rmask, 0, 0, &tv); return (status); }
rws@EXPO.LCS.MIT.EDU (Bob Scheifler) (01/27/89)
Lots of people have tripped over the problem of duplicate input events. We have fixed the problem here, an official patch should be available sometime next week (after we recover from the Conference and related meetings).