Search code examples
cx11xcb

xcb: wait/poll for reply OR event


XCB provides functions xcb_{wait,poll}_for_{reply,event}, but not xcb_{wait,poll}_for_reply_or_event, which would be useful in the following scenario:

  1. Send a batch of requests.
  2. Wait for replies. While waiting, handle incoming events.
  3. When the replies finally come in, handle the replies.

To implement steps 2 and 3, we could poll the underlying socket, as follows:

  1. Check for queued replies with xcb_poll_for_reply.
  2. Check for queued events with xcb_poll_for_event.
  3. poll the underlying socket.
  4. When poll indicates a read is possible, call xcb_poll_for_reply and/or xcb_poll_for_event and process the new data.

However, this seems to have a race condition. If a reply (but not an event) arrives between steps 1 and 2, it will get queued in XCB's internal data structures in step 2. Then we will end up blocking in poll even though there is data available to process. (Of course, a similar issue occurs if steps 1 and 2 are swapped.)

So, is there any efficient way to wait/poll for replies or events without this deficiency?


Solution

  • The problem can be solved by polling the underlying socket as described in the question, but with xcb_poll_for_event in step 2 replaced by xcb_poll_for_queued_event. (This might still be broken in multithreaded scenarios.)

    Alternatively, we can avoid needing to poll the socket by sending an unchecked dummy request that is guaranteed to fail, as follows:

    1. Send a batch of requests.
    2. Send an unchecked dummy request that will fail (e.g., MapWindow with an invalid window argument).
    3. Repeatedly call xcb_wait_for_event and handle incoming events while waiting for replies.
    4. Eventually, xcb_wait_for_event will return an error. If the sequence number in the error matches the sequence number of the dummy request, then ignore the error and continue to the next step; all replies must have arrived. Otherwise, the error is for a genuine request and should be handled appropriately.
    5. Read in all replies with xcb_poll_for_reply or the appropriate generated reply functions.

    The first approach is more efficient, but the second approach might lead to simpler code.