[Xcb] [PATCH libxcb v2 2/4] Add xcb_send_request_with_fds() and *_with_fds64()
Uli Schlachter
psychon at znc.in
Thu May 14 00:54:27 PDT 2015
Doing xcb_send_fd(), xcb_send_request() is racy. If two threads do this at the
same time, they could mix up their file descriptors. This commit makes it
possibly to fix this race by providing a single function which does everything
that is needed.
Signed-off-by: Uli Schlachter <psychon at znc.in>
---
src/xcb_out.c | 22 +++++++++++++++--
src/xcbext.h | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 90 insertions(+), 9 deletions(-)
diff --git a/src/xcb_out.c b/src/xcb_out.c
index 51d911f..06fae4c 100644
--- a/src/xcb_out.c
+++ b/src/xcb_out.c
@@ -204,15 +204,18 @@ static void send_fds(xcb_connection_t *c, unsigned int num_fds, int *fds)
#endif
}
-uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req)
+uint64_t xcb_send_request_with_fds64(xcb_connection_t *c, int flags, struct iovec *vector,
+ const xcb_protocol_request_t *req, unsigned int num_fds, int *fds)
{
uint64_t request;
uint32_t prefix[2];
int veclen = req->count;
enum workarounds workaround = WORKAROUND_NONE;
- if(c->has_error)
+ if(c->has_error) {
+ close_fds(num_fds, fds);
return 0;
+ }
assert(c != 0);
assert(vector != 0);
@@ -231,6 +234,7 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, req->ext);
if(!(extension && extension->present))
{
+ close_fds(num_fds, fds);
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_EXT_NOTSUPPORTED);
return 0;
}
@@ -261,6 +265,7 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
}
else if(longlen > xcb_get_maximum_request_length(c))
{
+ close_fds(num_fds, fds);
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_REQ_LEN_EXCEED);
return 0; /* server can't take this; maybe need BIGREQUESTS? */
}
@@ -307,6 +312,7 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
prepare_socket_request(c);
}
+ send_fds(c, num_fds, fds);
send_request(c, req->isvoid, workaround, flags, vector, veclen);
request = c->has_error ? 0 : c->out.request;
pthread_mutex_unlock(&c->iolock);
@@ -314,6 +320,18 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
}
/* request number are actually uint64_t internally but keep API compat with unsigned int */
+unsigned int xcb_send_request_with_fds(xcb_connection_t *c, int flags, struct iovec *vector,
+ const xcb_protocol_request_t *req, unsigned int num_fds, int *fds)
+{
+ return xcb_send_request_with_fds64(c, flags, vector, req, num_fds, fds);
+}
+
+uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req)
+{
+ return xcb_send_request_with_fds64(c, flags, vector, req, 0, NULL);
+}
+
+/* request number are actually uint64_t internally but keep API compat with unsigned int */
unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req)
{
return xcb_send_request64(c, flags, vector, req);
diff --git a/src/xcbext.h b/src/xcbext.h
index b2575f7..44d789e 100644
--- a/src/xcbext.h
+++ b/src/xcbext.h
@@ -68,13 +68,13 @@ enum xcb_send_request_flags_t {
*
* This function sends a new request to the X server. The data of the request is
* given as an array of @c iovecs in the @p vector argument. The length of that
- * array and the neccessary management information are given in the @p request
+ * array and the necessary management information are given in the @p request
* argument.
*
* When this function returns, the request might or might not be sent already.
* Use xcb_flush() to make sure that it really was sent.
*
- * Please note that this function is not the prefered way for sending requests.
+ * Please note that this function is not the preferred way for sending requests.
* It's better to use the generated wrapper functions.
*
* Please note that xcb might use index -1 and -2 of the @p vector array internally,
@@ -83,6 +83,37 @@ enum xcb_send_request_flags_t {
unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *request);
/**
+ * @brief Send a request to the server.
+ * @param c: The connection to the X server.
+ * @param flags: A combination of flags from the xcb_send_request_flags_t enumeration.
+ * @param vector: Data to send; must have two iovecs before start for internal use.
+ * @param request: Information about the request to be sent.
+ * @param num_fds: Number of additional file descriptors to send to the server
+ * @param fds: Additional file descriptors that should be send to the server.
+ * @return The request's sequence number on success, 0 otherwise.
+ *
+ * This function sends a new request to the X server. The data of the request is
+ * given as an array of @c iovecs in the @p vector argument. The length of that
+ * array and the necessary management information are given in the @p request
+ * argument.
+ *
+ * If @p num_fds is non-zero, @p fds points to an array of file descriptors that
+ * will be sent to the X server along with this request. After this function
+ * returns, all file descriptors sent are owned by xcb and will be closed
+ * eventually.
+ *
+ * When this function returns, the request might or might not be sent already.
+ * Use xcb_flush() to make sure that it really was sent.
+ *
+ * Please note that this function is not the preferred way for sending requests.
+ *
+ * Please note that xcb might use index -1 and -2 of the @p vector array internally,
+ * so they must be valid!
+ */
+unsigned int xcb_send_request_with_fds(xcb_connection_t *c, int flags, struct iovec *vector,
+ const xcb_protocol_request_t *request, unsigned int num_fds, int *fds);
+
+/**
* @brief Send a request to the server, with 64-bit sequence number returned.
* @param c: The connection to the X server.
* @param flags: A combination of flags from the xcb_send_request_flags_t enumeration.
@@ -92,13 +123,13 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect
*
* This function sends a new request to the X server. The data of the request is
* given as an array of @c iovecs in the @p vector argument. The length of that
- * array and the neccessary management information are given in the @p request
+ * array and the necessary management information are given in the @p request
* argument.
*
* When this function returns, the request might or might not be sent already.
* Use xcb_flush() to make sure that it really was sent.
*
- * Please note that this function is not the prefered way for sending requests.
+ * Please note that this function is not the preferred way for sending requests.
* It's better to use the generated wrapper functions.
*
* Please note that xcb might use index -1 and -2 of the @p vector array internally,
@@ -107,6 +138,38 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect
uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *request);
/**
+ * @brief Send a request to the server, with 64-bit sequence number returned.
+ * @param c: The connection to the X server.
+ * @param flags: A combination of flags from the xcb_send_request_flags_t enumeration.
+ * @param vector: Data to send; must have two iovecs before start for internal use.
+ * @param request: Information about the request to be sent.
+ * @param num_fds: Number of additional file descriptors to send to the server
+ * @param fds: Additional file descriptors that should be send to the server.
+ * @return The request's sequence number on success, 0 otherwise.
+ *
+ * This function sends a new request to the X server. The data of the request is
+ * given as an array of @c iovecs in the @p vector argument. The length of that
+ * array and the necessary management information are given in the @p request
+ * argument.
+ *
+ * If @p num_fds is non-zero, @p fds points to an array of file descriptors that
+ * will be sent to the X server along with this request. After this function
+ * returns, all file descriptors sent are owned by xcb and will be closed
+ * eventually.
+ *
+ * When this function returns, the request might or might not be sent already.
+ * Use xcb_flush() to make sure that it really was sent.
+ *
+ * Please note that this function is not the preferred way for sending requests.
+ * It's better to use the generated wrapper functions.
+ *
+ * Please note that xcb might use index -1 and -2 of the @p vector array internally,
+ * so they must be valid!
+ */
+uint64_t xcb_send_request_with_fds64(xcb_connection_t *c, int flags, struct iovec *vector,
+ const xcb_protocol_request_t *request, unsigned int num_fds, int *fds);
+
+/**
* @brief Send a file descriptor to the server in the next call to xcb_send_request.
* @param c: The connection to the X server.
* @param fd: The file descriptor to send.
@@ -114,9 +177,9 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
* After this function returns, the file descriptor given is owned by xcb and
* will be closed eventually.
*
- * FIXME: How the heck is this supposed to work in a thread-safe way? There is a
- * race between two threads doing xcb_send_fd(); xcb_send_request(); at the same
- * time.
+ * @deprecated This function cannot be used in a thread-safe way. Two threads
+ * that run xcb_send_fd(); xcb_send_request(); could mix up their file
+ * descriptors. Instead, xcb_send_request_with_fds() should be used.
*/
void xcb_send_fd(xcb_connection_t *c, int fd);
--
2.1.4
More information about the Xcb
mailing list