summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Harris <pharris@opentext.com>2010-02-09 16:27:22 -0500
committerPeter Harris <pharris@opentext.com>2010-02-11 14:35:37 -0500
commit367882fa32489ebafcd9afc04fbf732b02ceb33a (patch)
tree340fdb779ae2ace85efc24a1b0cbe3b0f92d871a
parentbe7e528eae62ddee14fa50f2c0e9036bafbc9f81 (diff)
Support xcb_discard_reply
This function is useful for dynamic language garbage collectors. Frequently a GC cycle may run before you want to block wainting for a reply. This function is also marginally useful for libxcb apps that issue speculative requests (eg. xlsclients). Reviewed-by: Jamey Sharp <jamey@minilop.net> Tested-by: Eamon Walsh <efw@eamonwalsh.com> Signed-off-by: Peter Harris <pharris@opentext.com>
-rw-r--r--src/xcb.h16
-rw-r--r--src/xcb_in.c110
2 files changed, 126 insertions, 0 deletions
diff --git a/src/xcb.h b/src/xcb.h
index f951276..35d8768 100644
--- a/src/xcb.h
+++ b/src/xcb.h
@@ -285,6 +285,22 @@ xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c);
*/
xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t cookie);
+/**
+ * @brief Discards the reply for a request.
+ * @param c: The connection to the X server.
+ * @param sequence: The request sequence number from a cookie.
+ *
+ * Discards the reply for a request. Additionally, any error generated
+ * by the request is also discarded (unless it was an _unchecked request
+ * and the error has already arrived).
+ *
+ * This function will not block even if the reply is not yet available.
+ *
+ * Note that the sequence really does have to come from an xcb cookie;
+ * this function is not designed to operate on socket-handoff replies.
+ */
+void xcb_discard_reply(xcb_connection_t *c, unsigned int sequence);
+
/* xcb_ext.c */
diff --git a/src/xcb_in.c b/src/xcb_in.c
index 26ab358..80f5523 100644
--- a/src/xcb_in.c
+++ b/src/xcb_in.c
@@ -409,6 +409,116 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
return ret;
}
+static void insert_pending_discard(xcb_connection_t *c, pending_reply **prev_next, uint64_t seq)
+{
+ pending_reply *pend;
+ pend = malloc(sizeof(*pend));
+ if(!pend)
+ {
+ _xcb_conn_shutdown(c);
+ return;
+ }
+
+ pend->first_request = seq;
+ pend->last_request = seq;
+ pend->workaround = 0;
+ pend->flags = XCB_REQUEST_DISCARD_REPLY;
+ pend->next = *prev_next;
+ *prev_next = pend;
+
+ if(!pend->next)
+ c->in.pending_replies_tail = &pend->next;
+}
+
+static void discard_reply(xcb_connection_t *c, unsigned int request)
+{
+ pending_reply *pend = 0;
+ pending_reply **prev_pend;
+ uint64_t widened_request;
+
+ /* We've read requests past the one we want, so if it has replies we have
+ * them all and they're in the replies map. */
+ if(XCB_SEQUENCE_COMPARE_32(request, <, c->in.request_read))
+ {
+ struct reply_list *head;
+ head = _xcb_map_remove(c->in.replies, request);
+ while (head)
+ {
+ struct reply_list *next = head->next;
+ free(head->reply);
+ free(head);
+ head = next;
+ }
+ return;
+ }
+
+ /* We're currently processing the responses to the request we want, and we
+ * have a reply ready to return. Free it, and mark the pend to free any further
+ * replies. */
+ if(XCB_SEQUENCE_COMPARE_32(request, ==, c->in.request_read) && c->in.current_reply)
+ {
+ struct reply_list *head;
+ head = c->in.current_reply;
+ c->in.current_reply = NULL;
+ c->in.current_reply_tail = &c->in.current_reply;
+ while (head)
+ {
+ struct reply_list *next = head->next;
+ free(head->reply);
+ free(head);
+ head = next;
+ }
+
+ pend = c->in.pending_replies;
+ if(pend &&
+ !(XCB_SEQUENCE_COMPARE(pend->first_request, <=, c->in.request_read) &&
+ (pend->workaround == WORKAROUND_EXTERNAL_SOCKET_OWNER ||
+ XCB_SEQUENCE_COMPARE(c->in.request_read, <=, pend->last_request))))
+ pend = 0;
+ if(pend)
+ pend->flags |= XCB_REQUEST_DISCARD_REPLY;
+ else
+ insert_pending_discard(c, &c->in.pending_replies, c->in.request_read);
+
+ return;
+ }
+
+ /* Walk the list of pending requests. Mark the first match for deletion. */
+ for(prev_pend = &c->in.pending_replies; *prev_pend; prev_pend = &(*prev_pend)->next)
+ {
+ if(XCB_SEQUENCE_COMPARE_32((*prev_pend)->first_request, >, request))
+ break;
+
+ if(XCB_SEQUENCE_COMPARE_32((*prev_pend)->first_request, ==, request))
+ {
+ /* Pending reply found. Mark for discard: */
+ (*prev_pend)->flags |= XCB_REQUEST_DISCARD_REPLY;
+ return;
+ }
+ }
+
+ /* Pending reply not found (likely due to _unchecked request). Create one: */
+ widened_request = (c->out.request & UINT64_C(0xffffffff00000000)) | request;
+ if(widened_request > c->out.request)
+ widened_request -= UINT64_C(1) << 32;
+
+ insert_pending_discard(c, prev_pend, widened_request);
+}
+
+void xcb_discard_reply(xcb_connection_t *c, unsigned int sequence)
+{
+ if(c->has_error)
+ return;
+
+ /* If an error occurred when issuing the request, fail immediately. */
+ if(!sequence)
+ return;
+
+ pthread_mutex_lock(&c->iolock);
+ discard_reply(c, sequence);
+ pthread_mutex_unlock(&c->iolock);
+}
+
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error)
{
int ret;