Bug 92652 - _XReadEvents is treating EAGAIN as a fatal error instead of just trying again
Summary: _XReadEvents is treating EAGAIN as a fatal error instead of just trying again
Alias: None
Product: XQuartz
Classification: Unclassified
Component: quartz-wm (show other bugs)
Version: 2.7.8 (xserver-1.16.4)
Hardware: x86-64 (AMD64) Mac OS X (All)
: high critical
Assignee: Jeremy Huddleston Sequoia
QA Contact: Jeremy Huddleston Sequoia
URL: http://xquartz.macosforge.org/trac/ti...
Depends on:
Reported: 2015-10-23 19:36 UTC by avde
Modified: 2016-05-29 08:52 UTC (History)
1 user (show)

See Also:
i915 platform:
i915 features:

program that crashes x-server (4.51 KB, text/plain)
2015-10-23 19:39 UTC, avde

Note You need to log in before you can comment on or make changes to this bug.
Description avde 2015-10-23 19:36:48 UTC
I am trying to use XEvent.type = ClientMessage to detect when a user closes a window. The attached c code is a small program to test this. It runs properly under other X servers I've tried but with XQuartz I get the following error:

XIO: fatal IO error 35 (Resource temporarily unavailable) on X server "/private/tmp/com.apple.launchd.OiaccbWRVW/org.macosforge.xquartz:0"

    after 21 requests (16 known processed) with 0 events remaining.

This problem is not new with release 2.7.8 and was also present in release 2.7.7. Not sure if it was occurring in earlier releases.
Comment 1 avde 2015-10-23 19:39:16 UTC
Created attachment 119156 [details]
program that crashes x-server
Comment 2 Jeremy Huddleston Sequoia 2016-05-28 21:54:39 UTC
AFAICT, there is no crash here.  Your title and the title of your attachment seem to indicate that this crashes the x server, but I don't see any such crash.
Comment 3 Jeremy Huddleston Sequoia 2016-05-28 21:55:57 UTC
Process 19612 stopped
* thread #1: tid = 0x1fe4a1, function: exit , stop reason = breakpoint 3.2
    frame #0: 0x00007fff9685f740 libsystem_c.dylib`exit(status=1) + 8 at exit.c:66 [opt]
(lldb) bt
* thread #1: tid = 0x1fe4a1, function: exit , stop reason = breakpoint 3.2
  * frame #0: 0x00007fff9685f740 libsystem_c.dylib`exit(status=1) + 8 at exit.c:66 [opt]
    frame #1: 0x000000010008fbac libX11.6.dylib`_XDefaultIOError(dpy=0x000062100001cd88) + 316 at XlibInt.c:1258 [opt]
    frame #2: 0x000000010008ac8b libX11.6.dylib`_XIOError(dpy=0x000062100001cd00) + 251 at XlibInt.c:1464 [opt]
    frame #3: 0x0000000100084cfe libX11.6.dylib`_XReadEvents(dpy=<unavailable>) + 1566 at xcb_io.c:414 [opt]
    frame #4: 0x000000010004b4cd libX11.6.dylib`XNextEvent(dpy=0x000062100001cd00, event=<unavailable>) + 173 at NextEvent.c:50 [opt]
    frame #5: 0x0000000100000c42 crashit`event_loop + 98
Comment 4 Jeremy Huddleston Sequoia 2016-05-28 22:06:07 UTC
Why is EAGAIN a fatal IO Error?
Comment 5 Jeremy Huddleston Sequoia 2016-05-28 22:12:48 UTC
xcb_generic_event_t *event;
dpy->xcb->event_waiter = 1;
event = xcb_wait_for_event(dpy->xcb->connection);
/* It appears that classic Xlib respected user
 * locks when waking up after waiting for
 * events. However, if this thread did not have
 * any user locks, and another thread takes a
 * user lock and tries to read events, then we'd
 * deadlock. So we'll choose to let the thread
 * that got in first consume events, despite the
 * later thread's user locks. */
InternalLockDisplay(dpy, /* ignore user locks */ 1);
dpy->xcb->event_waiter = 0;
ConditionBroadcast(dpy, dpy->xcb->event_notify);
        _XIOError(dpy);   // Here's where we're bailing.

So xcb_wait_for_event() is returning NULL.
Comment 6 Jeremy Huddleston Sequoia 2016-05-28 22:24:41 UTC
If I keep retrying, xcb_wait_for_event eventually errs with ETIMEDOUT instead... hmm...
Comment 7 Jeremy Huddleston Sequoia 2016-05-28 22:40:35 UTC
_xcb_conn_wait is returning false in here:

    while(!(ret = get_event(c)))
        if(!_xcb_conn_wait(c, &c->in.event_cond, 0, 0))

FWIW, this is true for both the poll and select codepaths.

Need to dig into this more later.
Comment 8 Uli Schlachter 2016-05-29 08:02:45 UTC
Could you try moving the code that sets up WM_PROTOCOLS / WM_DELETE_WINDOW from event_loop() into create_window() (*before* the call to XMapWindow())? (No, I do not have a Mac where I could test this myself)
Comment 9 Jeremy Huddleston Sequoia 2016-05-29 08:07:26 UTC
Looks like the WM is calling XKillClient():

(lldb) bt
* thread #3: tid = 0x20aab8, function: CloseDownConnection , stop reason = breakpoint 5.1
  * frame #0: 0x0000000101f44780 X11.bin`CloseDownConnection
    frame #1: 0x0000000101e1f804 X11.bin`CloseDownClient + 484
    frame #2: 0x0000000101e3a84a X11.bin`ProcKillClient + 314
    frame #3: 0x0000000101e1ebf4 X11.bin`Dispatch + 1172

And digging into quartz-wm's code, we see:

- (void) do_close:(Time)timestamp
    TRACE ();

    if (_does_wm_delete_window)
        XEvent e;

        e.xclient.type = ClientMessage;
        e.xclient.window = _id;
        e.xclient.message_type = atoms.wm_protocols;
        e.xclient.format = 32;
        e.xclient.data.l[0] = atoms.wm_delete_window;
        e.xclient.data.l[1] = timestamp;

        XSendEvent (x_dpy, _id, False, 0, &e);
        XKillClient (x_dpy, _id);
Comment 10 Jeremy Huddleston Sequoia 2016-05-29 08:13:04 UTC
Uli, yep, moving the XSetWMProtocols() ahead of the XMapWindow() causes this to behave as expected.  So now I just need to figure out what the correct behavior is here.  My ICCCM is a bit rusty, but this is wither a bug in the WM or in the test app.
Comment 11 Jeremy Huddleston Sequoia 2016-05-29 08:43:40 UTC

Should be in XQuartz 2.7.10_beta2 (not yet released).
Comment 12 Uli Schlachter 2016-05-29 08:52:56 UTC
"§ WM_PROTOCOLS Property" doesn't talk about changes while "§ 4.1.2. Client Properties" says "The window manager will examine the contents of these properties when the window makes the transition from the Withdrawn state and will monitor some properties for changes while the window is in the Iconic or Normal state."

"Some properties" is quite vague, but apparently (since this works on Linux), most(?) WMs there do.

Thanks for fixing this in XQuartz.

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.