Skip to content

Async support #1

@notgull

Description

@notgull

libxcb is just a wrapper around a socket with some external state, so theoretically we should be able to implement CanBeAsyncDisplay on XcbDisplay (and also XlibDisplay). In practice, it ends up being more complicated.

Here are the methods of CanBeAsyncDisplay in order of difficulty of implementation:

  • try_wait_for_reply_raw, try_wait_for_event - Trivial. Use xcb_poll_for_reply64 or xcb_poll_for_event, and return AsyncStatus::Read if one doesn't come up.
  • try_send_request_raw - Hard. libxcb on its own does not support non-blocking writes. However, it is possible to get the file descriptor of the connection, preform non-blocking writes on that, and then use xcb_writev to update the connection's sequence number. Note that this restricts async support to being Unix-only, since Windows async systems use Windows sockets.
  • try_flush - Hard. xcb_flush preforms a blocking flush of the internal connection buffer, and there is no other way to access that buffer from the external API. That being said, while using the async API, we probably won't involve the internal buffer too much. We could have a check saying, "if the socket has been taken since we last used it, this flush will be blocking", and then also make sure to flush our own buffer. Note that this means that we'd have to make our own buffer for writes here, which I expected to have to do anyways.
  • format_request - Very Hard. We need the sequence number to do formatting, which we get from xcb_take_socket (note that we need to call xcb_take_socket for writing above). First, it preforms a flush of the internal buffer which, as discussed above, can be problematic. Although that can be avoided, we do have to deal with the case where a malicious user takes the socket and passes in a callback that blocks for an extended period of time. We could probably also check to see if the socket has been taken and, if so, expect to block. That being said, we also may need to query for extensions and maximum request length, which use internal consistent state and can't be done without blocking. In these cases, this moves up to Impossible.
  • try_check_for_error - Very Hard. Basically the same as flushing and then reading.
  • try_generate_xid, try_maximum_request_length - Impossible. xcb_generate_id and xcb_maximum_request_length rely on internal state that needs to be kept consistent between breadx and libxcb. Even if we could get around this by emulating it on the breadx end, foreign code could call these functions before/after the Display in instantiated and mess up our state.

The Impossibles are generally cold operations, so we could probably eat the cost of using a threadpool and import blocking or using tokio's spawn_blocking function. The Hards require a new type at minimum to wrap XcbDisplay, and probably some kind of system for taking the XCB socket.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions