From d64f4d3939845c20dac5b7d8bf648b11cb69c192 Mon Sep 17 00:00:00 2001
From: Peter Harris <pharris@opentext.com>
Date: Mon, 1 Feb 2010 16:51:02 -0500
Subject: [PATCH] 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. xlsatoms).
---
 src/xcb.h    |   12 +++++++++
 src/xcb_in.c |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 83 insertions(+), 0 deletions(-)

diff --git a/src/xcb.h b/src/xcb.h
index f951276..ffe3b52 100644
--- a/src/xcb.h
+++ b/src/xcb.h
@@ -285,6 +285,18 @@ 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 cookie: The request cookie.
+ *
+ * The xcb_void_cookie_t cookie supplied to this function must have resulted
+ * from a call to an xcb_ function that expects a reply. Additionally, any
+ * error generated by the request is also discarded. This function will not
+ * block even if the reply is not yet available.
+ */
+void xcb_discard_reply(xcb_connection_t *c, xcb_void_cookie_t cookie);
+
 
 /* xcb_ext.c */
 
diff --git a/src/xcb_in.c b/src/xcb_in.c
index 26ab358..e15eee2 100644
--- a/src/xcb_in.c
+++ b/src/xcb_in.c
@@ -409,6 +409,77 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
     return ret;
 }
 
+static void discard_reply(xcb_connection_t *c, unsigned int request)
+{
+    pending_reply *pend = 0;
+
+    /* 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;
+
+	return;
+    }
+
+    /* Walk the list of pending requests. Mark the first match for deletion. */
+    for (pend = c->in.pending_replies; pend; pend = pend->next) {
+	if (XCB_SEQUENCE_COMPARE_32(pend->first_request, <, request))
+	    continue;
+	if (XCB_SEQUENCE_COMPARE_32(pend->first_request, ==, request))
+	    pend->flags |= XCB_REQUEST_DISCARD_REPLY;
+	break;
+    }
+}
+
+void xcb_discard_reply(xcb_connection_t *c, xcb_void_cookie_t cookie)
+{
+    if(c->has_error)
+        return;
+
+    /* If an error occurred when issuing the request, fail immediately. */
+    if(!cookie.sequence)
+	return;
+    
+    pthread_mutex_lock(&c->iolock);
+    discard_reply(c, cookie.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;
-- 
1.6.6.1
