Re: Help with external socket handling and xcb
Uli Schlachter <[email protected]> Wed, 8 Sep 2021 17:58:39 +0200
| Newsgroups | gmane.comp.freedesktop.xcb |
|---|---|
| Message-ID | <[email protected]> |
Hi, Am 08.09.21 um 15:29 schrieb Carlo Wood: [...] > Unfortunately, not many libraries that provide protocol handling > provide such an API - most of them insist on also handling the socket > side of things; and as far as I can tell xcb is like that too. This might be a bit off-topic, but how would such a library be thread-safe? xcb does quite a dance internally to provide a thread-safe API and hide all these implementation details. For example, xcb_get_input_focus_reply() blocks until the reply was received from the X11 server (and does so in a way that does not block other threads from using the X11 connection). Sure, you can use xcb_poll_for_reply() to check whether the reply was received yet, but going down this route results in quite spaghetti-y code. > Using xcb_get_file_descriptor I can start monitoring it for input > (aka, a function is called whenever the socket is readable). > At this point I NEED a substantial amount of data to be read > from the socket (preferably everything, so that epoll won't report > it being readable again). I understand that this is possible by > calling xcb_poll_for_event until it returns NULL. Again: I'd rather > do the reading and buffering of the data myself and then feed > that to xcb to decode it; but this will work... or does it? Well, there is no public API for this. Internally, _xcb_in_read() reads from the socket into a buffer and then reads individual packets from this via read_packet(). However, inside a packet, the remainder of a packet is again read blockingly (see call to _xcb_in_read_block()). The expectation here is that the X11 server will write the whole packet in one go. This is necessary because X11 packets can be quite large, certainly larger than the 4 KiB that is the default read buffer size. Also: Your approach would mean that the data would have to be copied again. In some cases (the _xcb_in_read_block() call I mention above), libxcb can read the data directly into its final place; avoiding an extra copy. > Because I assume that xcb internally will keep reading the socket > until that returns that there is no data left to read. And in order > for that not to be blocking, the fd has to be non-blocking (which > is obviously what I normally want). xcb actually calls poll()/select() before doing anything with the socket to make sure that following call will not block "too badly". Anything else would again interfere with thread-safety. However, functions like xcb_wait_for_reply() still call this code in a loop until they are done with what they want to do. I don't really see how this could be done with an external "loop driver". Every function returning some kind of EAGAIN? I wouldn't want to try to use such an API. > However, doesn't there need to be data written to the socket too? > When does that happen? Whenever it needs to be. There is a buffer for outgoing data. When that is full, the current call blocks until the buffered data was written. Also, xcb_flush() will, well, flush the buffer. So basically: Any function that sends a request can block waiting for some data to be written. > With the current set up I can't monitor the > socket to see if it writable because it is ALWAYS writable. I can > only do this when xcb tells me (through a callback) that it NEEDS > me to monitor the socket because it has something to write (and > also tells me when I can stop monitoring the fd again). What would you want a function like xcb_get_input_focus() to return when it needs to write some data, but the buffer is currently full? > So, since it doesn't do that (as far as I know), it will have to > make sure that every time it has something to write, it writes > ALL of it before returning to my code... and that sounds very > blocking. How can this ever work reliable with a non-blocking > fd? Or is xcb just calling write in a loop until it wrote everything > it has to write (also when write returns EAGAIN)? Basically yes. _xcb_conn_wait() calls write_vec() to write some data whenever poll()/select() say that the FD is writeable and there is some pending data to send. Here is the loop that then calls _xcb_conn_wait() until everything was written: https://github.com/freedesktop/xcb-libxcb/blob/ee9dfc9a7658e7fe75d27483bb5ed1ba4d1e2c86/src/xcb_out.c#L457-L458 > If anyone can give me pointers, or maybe tell me about a part of > the API that I missed that is suitable for this, that would be great! For non-blockingly waiting for a reply to a request: Do you know about xcb_poll_for_reply()? There is an example on how to use that to get a callback-based "your reply arrived"-API here: https://gitlab.freedesktop.org/xorg/lib/libxcb/-/issues/52#note_712068 (Hm, it seems like this code does not handle sequence number overflows...) I hope some of this helps. Cheers, Uli -- If you have to type the letters "A-E-S" into your source code, you're doing it wrong.