Bug 34715

Summary: XtAppMainLoop doesn't work without a Display
Product: xorg Reporter: Jordan Hayes <jordan>
Component: Lib/XtAssignee: Xorg Project Team <xorg-team>
Status: RESOLVED FIXED QA Contact: Xorg Project Team <xorg-team>
Severity: enhancement    
Priority: medium CC: jordan
Version: 7.6 (2010.12)   
Hardware: All   
OS: All   
Whiteboard:
i915 platform: i915 features:

Description Jordan Hayes 2011-02-25 05:05:17 UTC
The manual page for XtAppMainLoop says that it's just a simple loop that calls

    XEvent event;
    XtAppNextEvent(app, &event);
    XtDispatchEvent(&event);

... until XtAppGetExitFlag() returns true.

And looking at the code in libXt-1.0.9/src/Event.c seems to show that to be the case.

But: XtAppNextEvent() doesn't return until there's an actual XEvent to be handled; it handles Xt-internal events (inputs, timers, signals) itself, but doesn't return (because of course, those aren't XEvents).  Which means that the exit flag doesn't get a chance to break the loop until/unless there's an actual XEvent.

If you're not using a Display at all in your app, you lose: timers or input sources can't signal the main loop to exit using this mechanism.

A workaround -- and what I would suggest XtAppMainLoop be fixed to do instead -- is code that looks more like this:

    for (;;) {
        XtAppProcessEvent(app, XtIMAll);
        if (XtAppGetExitFlag(app))
            break;
    }

XtAppProcessEvent() returns when *any* event has been dispatched, not just ones that involve a Display.  For the source tree, it would look something like this:

void XtAppMainLoop(XtAppContext app) {
    LOCK_APP(app);
    do {
        XtAppProcessEvent(app, XtIMAll);
    } while (! app->exit_flag);
    UNLOCK_APP(app);
}

Here's some code that shows the problem:

#include <X11/Intrinsic.h>

static void _Tick(XtPointer baton, XtIntervalId* id) {
    static int count = 0;

    printf("%d beep!\n", ++count);

    XtAppContext app = (XtAppContext)baton;
    if (3 == count)
        XtAppSetExitFlag(app);
    else
        XtAppAddTimeOut(app, 3000, _Tick, app);
}

int main(int argc, char** argv) {
    XtToolkitInitialize();
    XtAppContext app = XtCreateApplicationContext();
    XtAppAddTimeOut(app, 3000, _Tick, app);

#ifdef    SHOWBUG
    XtAppMainLoop(app);
#else
    for (;;) {
        XtAppProcessEvent(app, XtIMAll);
        if (XtAppGetExitFlag(app))
            break;
    }
#endif

    return 0;
}

The patch is simple:

$ rcsdiff -c Event.c
===================================================================
RCS file: Event.c,v
retrieving revision 1.1
diff -c -r1.1 Event.c
*** Event.c     2011/02/25 12:58:32     1.1
--- Event.c     2011/02/25 12:59:51
***************
*** 1553,1563 ****

      LOCK_APP(app);
      do {
!       XtAppNextEvent(app, &event);
! #ifdef XTHREADS
!       /* assert(app == XtDisplayToApplicationContext(event.xany.display)); */
! #endif
!       XtDispatchEvent(&event);
      } while(app->exit_flag == FALSE);
      UNLOCK_APP(app);
  }
--- 1553,1559 ----

      LOCK_APP(app);
      do {
!       XtAppProcessEvent(app, XtIMAll);
      } while(app->exit_flag == FALSE);
      UNLOCK_APP(app);
  }
Comment 1 Alan Coopersmith 2011-02-25 13:00:17 UTC
I don't believe libXt was ever designed, intended, or tested for use in
non-GUI applications with no open display.   This sounds like a request
to add support for that, not a bug in failing to meet the design.
Comment 2 Jordan Hayes 2011-02-25 13:27:12 UTC
(In reply to comment #1)
> I don't believe libXt was ever designed, intended, or tested
> for use in non-GUI applications with no open display.

It was certainly discussed (and encouraged by Consortium engineering staff) in the late 80's (X11R2 timeframe), many years before the new ExitFlag mechanism was added.  If you don't want to call it a bug, it's at least inconsistent with the documentation.

For what it's worth, I do believe that the addition of the XtAppContext was specifically intended to more generally support event-driven programming.  Later, Tk was inspired by XtAppMainLoop() and John Ousterhaut similarly migrated Tcl_MainLoop out of the graphical Tk and into the core Tcl base.

I wrote a Usenix paper in 1994 about this kind of thing:

http://bitway.com/jordan/papers/USENIX/AppDev.html
Comment 3 Alan Coopersmith 2011-02-25 14:22:58 UTC
I stand corrected - unfortunately, we have little information available to
most developers today about what the Consortium did 20 years ago, and Xt
itself is barely maintained for binary compatibility, with no one who knows
much about it willing to spend time working on maintaining it.
Comment 4 walter 2011-03-05 07:15:57 UTC
NTL the request is reasonable. I did some tests and found no problems so we should give it a go. LibXT did not have many changes inrecent times so it is ok to start fixing more exotic problems.
Comment 5 Jordan Hayes 2011-03-05 07:49:49 UTC
This issue came up with the addition of the XtAppSetExitFlag() call; I can't tell from the history when that was added or by who.  At the time, XtAppMainLoop() was changed from what it was originally to include the test for the exit flag; that's the genesis of this bug.
Comment 6 Alan Coopersmith 2011-03-06 11:30:37 UTC
Looks like XtAppSetExitFlag() was added by the X Consortium for the initial
X11R6.0 release in 1994 - it's in there but not in X11R5.
Comment 7 Jordan Hayes 2011-03-06 12:27:38 UTC
Ok ... so ... can we fix it? :-)
Comment 8 Alan Coopersmith 2011-03-06 12:56:08 UTC
Patch submitted to xorg-devel for review:
  http://lists.x.org/archives/xorg-devel/2011-March/019981.html

I've not seen any noticable issues with the various Xt-based apps I ran
from the X.Org app/* set, but not really fully-exercised them either, or
more complex apps / toolkits like Motif.
Comment 9 Jeremy Huddleston Sequoia 2011-10-07 17:48:30 UTC
commit 42c611d9f8c80528f6e36ceb0ce245b06e8b1e8e
Author: Jordan Hayes <jordan@bitway.com>
Date:   Sun Mar 6 11:35:47 2011 -0800

    Bug 34715: XtAppMainLoop doesn't work without a Display
    
    https://bugs.freedesktop.org/show_bug.cgi?id=34715
    
    XtAppNextEvent() doesn't return until there's an actual XEvent to be
    handled; it handles Xt-internal events (inputs, timers, signals)
    itself, but doesn't return (because of course, those aren't XEvents).
    Which means that the exit flag doesn't get a chance to break the loop
    until/unless there's an actual XEvent.
    
    Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.