XtAppPending can block uninterruptably.
Submitted by Stephen Turnbull
Assigned to Jeremy Huddleston Sequoia
Description
Sorry about the version; according to MacPorts it's libXt-1.0.5_0. I don't know what release of X.org that corresponds to.
Mac OS X 10.5.6 (Intel Core Duo MacBook Pro) and Mac OS X 10.4.11 + security update 2008-008 (iBook G4). This seems to have started with recent updates to Mac OS X (10.5.5 or 10.5.6 for Leopard, and security update 2008-007 or so for Tiger). However, I've been frequently updating X.org with MacPorts as well, so it might be recent X.org versions rather than any change in Mac OS X.
In XEmacs current head version, if I start "xemacs -vanilla" (ie, no user configuration), xemacs will initialize itself and start its toplevel read-eval-redisplay loop. This eventually settles into a loop which calls XtAppPending, which calls _XtWaitForSomething, which calls IOWait, which is a wrapper for poll(2) in the MacPorts configuration of libXt.
This works fine (normal editing etc is possible) until I start an asynchronous process, for example with the Lisp code
(start-process "echo" nil "echo")
The process does get started AFAICT, and XEmacs reports the status return from start-process, then freezes in a tight loop in _XtWaitForSomething:
^Z
Program received signal SIGTSTP, Stopped (user).
0x929b9e0e in poll$UNIX2003 ()
(gdb) next
Single stepping until exit from function poll$UNIX2003,
which has no line number information.
IoWait (wt=<value temporarily unavailable, due to optimizations>, wf=<value temporarily unavailable, due to optimizations>) at NextEvent.c:353
353 }
(gdb)
_XtWaitForSomething (app=0xe111a0, ignoreEvents=0, ignoreTimers=<value temporarily unavailable, due to optimizations>, ignoreInputs=<value temporarily unavailable, due to optimizations>, ignoreSignals=1, block=0, drop_lock=0, howlong=0x0) at NextEvent.c:619
619 if (nfds == -1) {
(gdb) next
696 if (nfds == 0) {
(gdb)
706 if (block && howlong != NULL)
(gdb)
709 if (ignoreInputs && ignoreEvents) {
(gdb)
715 FindInputs (app, &wf, nfds,
(gdb)
** Stepping over inlined function code. **
466 if (!ignoreInputs) {
(gdb)
454 for (ii = 0; ii < wf->
num_dpys; ii++, fdlp++) {
(gdb)
466 if (!ignoreInputs) {
(gdb)
FindInputs [inlined] () at NextEvent.c:467
467 fdlp = &wf->fdlist[wf->num_dpys];
(gdb)
FindInputs [inlined] () at NextEvent.c:468
468 for (ii = wf->num_dpys; ii < wf->
fdlistlen; ii++, fdlp++) {
(gdb)
FindInputs [inlined] () at NextEvent.c:467
467 fdlp = &wf->fdlist[wf->num_dpys];
(gdb)
FindInputs [inlined] () at NextEvent.c:468
468 for (ii = wf->num_dpys; ii < wf->
fdlistlen; ii++, fdlp++) {
(gdb)
FindInputs [inlined] () at NextEvent.c:470
470 if (fdlp->revents) {
(gdb)
FindInputs [inlined] () at NextEvent.c:468
468 for (ii = wf->num_dpys; ii < wf->
fdlistlen; ii++, fdlp++) {
(gdb)
FindInputs [inlined] () at NextEvent.c:470
470 if (fdlp->revents) {
(gdb)
FindInputs [inlined] () at NextEvent.c:471
471 if (fdlp->revents & (XPOLL_READ|POLLHUP|POLLERR)
(gdb)
FindInputs [inlined] () at NextEvent.c:477
477 if (fdlp->revents & XPOLL_WRITE)
(gdb)
482 if (condition) {
(gdb)
FindInputs [inlined] () at NextEvent.c:468
468 for (ii = wf->num_dpys; ii < wf->
fdlistlen; ii++, fdlp++) {
(gdb)
719 if (dpy_no >= 0 || found_input) {
(gdb)
588 AdjustTimes (app, block, howlong, ignoreTimers, &wt);
(gdb)
** Stepping over inlined function code. **
590 if (block && app->block_hook_list) {
(gdb)
608 if (app->rebuild_fdlist)
(gdb)
612 if (drop_lock) {
(gdb)
618 nfds = IoWait (&wt, &wf);
(gdb)
Heh-heh, heh-heh, heh-heh Beavis, let's do that again! :-(
If I enable printing of signals in gdb, I see SIGIO being delivered over and over again, but I can't tell what those are for. Moving the mouse or clicking or typing in the window should queue up input I suppose, but doesn't break the infloop although gdb reports it's generating SIGIOs. nfds and wf look like this in the infloop:
(gdb) print nfds $3 = 1 (gdb) print wf $4 = {fdlist = 0xeb1b50, stack = 0xbffff170, fdlistlen = 3, num_dpys = 1} gdb) print wf.fdlist[0] $5 = {fd = 6, events = 1, revents = 0} (gdb) print wf.fdlist[1] $6 = {fd = 4, events = 195, revents = 0} (gdb) print wf.fdlist[2] $7 = {fd = 7, events = 195, revents = 32}
revents = 32 is POLLNVAL.
lsof identifies the fds in question as
xemacs 88335 steve 4 PIPE 0x5464f40 16384
xemacs 88335 steve 6u unix 0x5a6d198 0t0 ->0xbc15cc0
xemacs 88335 steve 7u CHR 15,3 0t0 73199364 /dev/ptmx
The PIPE in fd=4 is used internally by XEmacs's own event loop, the unix socket fd=6 is X I assume, and the character special fd=7 is associated with the subprocess, but I don't know the details.
This configuration is just ignored by _XtWaitForSomething, and it just keeps reproducing itself (I don't understand poll, so I can't guess whether the signal is being reraised within every loop or if the state of the fd persists over the loop). Thus we just skip all the way down to goto WaitLoop ... lather, rinse, repeat ....
XtAppPending promises not to block, so I think this is a pretty serious bug.
See also Bug #13471.