How do I interrupt xcb_wait_for_event?
Asked Answered
J

2

5

In a separate thread (std::thread), I have an event loop that waits on xcb_wait_for_event. When the program exits, I'd like to shut things down nicely by interrupting (I have a solution that sets a thread-local variable, and checkpoints in the loop throw an exception), and then joining my event thread into the main thread. The issue is xcb_wait_for_event; I need a way to return from it early, or I need an alternative to the function.

Can anyone suggest a solution? Thanks for your help!

Jinn answered 22/5, 2015 at 1:47 Comment(0)
E
4

A neater way would be to do something like this (the code snippet is extracted from some code I am currently working on):

void QXcbEventQueue::sendCloseConnectionEvent() const {
    // A hack to close XCB connection. Apparently XCB does not have any APIs for this?
    xcb_client_message_event_t event;
    memset(&event, 0, sizeof(event));

    event.response_type = XCB_CLIENT_MESSAGE;
    event.format = 32;
    event.sequence = 0;
    event.window = m_connection->clientLeader();
    event.type = m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION);
    event.data.data32[0] = 0;

    xcb_connection_t *c = m_connection->xcb_connection();
    xcb_send_event(c, false, m_connection->clientLeader(),
                   XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event));
    xcb_flush(c); }

For _QT_CLOSE_CONNECTION use your own atom to signal an exit and in my case clientLeader() is some invisible window that is always present on my X11 connection. If you don't have any invisible windows that could be reused for this purpose, create one :)

With this you can terminate the thread with xcb_wait_for_event when you see this special event arriving.

Egest answered 13/10, 2018 at 19:56 Comment(3)
That's a pretty good idea, thanks. Certainly more elegant than the solution I came up with back then. If I return to the world of X11 window managers, I'll keep it in mind!Jinn
To put it more succinctly for posterity: To interrupt a thread waiting on an event, make an event happen. If it's a special event for this purpose, exit the wait loop. Send the event to a window you're sure exists. In the case you're writing a window manager, I'm sure this could be sent to the root window, or perhaps the supporting window (the one you set _NET_SUPPORTING_WM_CHECK on for EWMH compliance).Jinn
What's the receiving part of it? Is there some kind of mask that has to be set? I'm not getting the event in the xcb_wait_for_event.Anthropoid
J
7

I believe I've come up with a suitable solution. I've replaced xcb_wait_for_event with the following function:

xcb_generic_event_t *WaitForEvent(xcb_connection_t *XConnection)
{
    xcb_generic_event_t *Event = nullptr;

    int XCBFileDescriptor = xcb_get_file_descriptor(XConnection);
    fd_set FileDescriptors;

    struct timespec Timeout = { 0, 250000000 }; // Check for interruptions every 0.25 seconds

    while (true)
    {
        interruptible<std::thread>::check();

        FD_ZERO(&FileDescriptors);
        FD_SET(XCBFileDescriptor, &FileDescriptors);

        if (pselect(XCBFileDescriptor + 1, &FileDescriptors, nullptr, nullptr, &Timeout, nullptr) > 0)
        {
            if ((Event = xcb_poll_for_event(XConnection)))
                break;
        }
    }

    interruptible<std::thread>::check();

    return Event;
}

Making use of xcb_get_file_descriptor, I can use pselect to wait until there are new events, or until a specified timeout has occurred. This method incurs negligible additional CPU costs, resting at a flat 0.0% (on this i7). The only "downside" is having to wait a maximum of 0.25 seconds to check for interruptions, and I'm sure that limit could be safely lowered.

Jinn answered 22/5, 2015 at 17:41 Comment(4)
Why is it necessary to wait for events with XCB? I'm currently calling xcb_image_get() in a very tight loop, and my computer freezes (I assume because I have to wait for a reply). This doesn't happen with X11's XGetImage()!Semple
Xlib waits for replies too, but it automatically blocks the current thread to receive them. XCB is asynchronous, and so it allows you to wait for replies whenever you decide to. Your computer is freezing not because XCB is waiting for events, but because you're spamming the connection with requests.Jinn
Thank you! Finding information on the X protocol (other than the formal specification) is very hardSemple
Whoa, a year and half later, but you're very welcome! :)Jinn
E
4

A neater way would be to do something like this (the code snippet is extracted from some code I am currently working on):

void QXcbEventQueue::sendCloseConnectionEvent() const {
    // A hack to close XCB connection. Apparently XCB does not have any APIs for this?
    xcb_client_message_event_t event;
    memset(&event, 0, sizeof(event));

    event.response_type = XCB_CLIENT_MESSAGE;
    event.format = 32;
    event.sequence = 0;
    event.window = m_connection->clientLeader();
    event.type = m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION);
    event.data.data32[0] = 0;

    xcb_connection_t *c = m_connection->xcb_connection();
    xcb_send_event(c, false, m_connection->clientLeader(),
                   XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event));
    xcb_flush(c); }

For _QT_CLOSE_CONNECTION use your own atom to signal an exit and in my case clientLeader() is some invisible window that is always present on my X11 connection. If you don't have any invisible windows that could be reused for this purpose, create one :)

With this you can terminate the thread with xcb_wait_for_event when you see this special event arriving.

Egest answered 13/10, 2018 at 19:56 Comment(3)
That's a pretty good idea, thanks. Certainly more elegant than the solution I came up with back then. If I return to the world of X11 window managers, I'll keep it in mind!Jinn
To put it more succinctly for posterity: To interrupt a thread waiting on an event, make an event happen. If it's a special event for this purpose, exit the wait loop. Send the event to a window you're sure exists. In the case you're writing a window manager, I'm sure this could be sent to the root window, or perhaps the supporting window (the one you set _NET_SUPPORTING_WM_CHECK on for EWMH compliance).Jinn
What's the receiving part of it? Is there some kind of mask that has to be set? I'm not getting the event in the xcb_wait_for_event.Anthropoid

© 2022 - 2024 — McMap. All rights reserved.