[PATCH wayland 1/4 v2] client: Introduce proxy wrappers

Jonas Ådahl jadahl at gmail.com
Wed Apr 27 07:37:38 UTC 2016


Using the libwayland-client client API with multiple threads with
thread local queues are prone to race conditions.

The problem is that one thread can read and dispatch events after
another thread creates the wl_callback object but before it sets the
queue. This could potentially cause the events to be dropped completely
if the event is dispatched before the creating thread sets the
implementation, or that the event is dispatched on the wrong queue which
might run on another thread.

This patch introduces API to solve this case by introducing "proxy
wrappers". In short, a proxy wrapper is a wl_proxy struct that will
never itself emit any events, but may be used by the client to set a
queue, and use it instead of the original proxy when sending requests
that creates new proxy objects. When sending requests, the wrapper will
work in the same way as the normal proxy object, but the proxy created
by sending a request (for example wl_display.sync) will default to the
same proxy queue as the wrapper.

Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---

Changes since v1:

 - Function type signatures changed to use more void * since
   in all cases the wrapper needs to be type casted anyway. Using
   void * avoids several casts.
 - Require destroying wrappers using wl_proxy_wrapper_destroy()
   

Jonas

 src/wayland-client-core.h |   6 +++
 src/wayland-client.c      | 135 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 140 insertions(+), 1 deletion(-)

diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h
index 91f7e7b..b1d6515 100644
--- a/src/wayland-client-core.h
+++ b/src/wayland-client-core.h
@@ -132,6 +132,12 @@ struct wl_proxy *
 wl_proxy_create(struct wl_proxy *factory,
 		const struct wl_interface *interface);
 
+void *
+wl_proxy_create_wrapper(void *proxy);
+
+void
+wl_proxy_wrapper_destroy(void *proxy_wrapper);
+
 struct wl_proxy *
 wl_proxy_marshal_constructor(struct wl_proxy *proxy,
 			     uint32_t opcode,
diff --git a/src/wayland-client.c b/src/wayland-client.c
index 33033e7..f21b5f7 100644
--- a/src/wayland-client.c
+++ b/src/wayland-client.c
@@ -51,7 +51,8 @@
 
 enum wl_proxy_flag {
 	WL_PROXY_FLAG_ID_DELETED = (1 << 0),
-	WL_PROXY_FLAG_DESTROYED = (1 << 1)
+	WL_PROXY_FLAG_DESTROYED = (1 << 1),
+	WL_PROXY_FLAG_WRAPPER = (1 << 2),
 };
 
 struct wl_proxy {
@@ -428,6 +429,8 @@ proxy_destroy(struct wl_proxy *proxy)
  *
  * \param proxy The proxy to be destroyed
  *
+ * \c proxy must not be a proxy wrapper.
+ *
  * \memberof wl_proxy
  */
 WL_EXPORT void
@@ -435,6 +438,12 @@ wl_proxy_destroy(struct wl_proxy *proxy)
 {
 	struct wl_display *display = proxy->display;
 
+	if (proxy->flags == WL_PROXY_FLAG_WRAPPER) {
+		wl_log("warning: tried to destroy wrapper with "
+		       "wl_proxy_destroy()\n");
+		return;
+	}
+
 	pthread_mutex_lock(&display->mutex);
 	proxy_destroy(proxy);
 	pthread_mutex_unlock(&display->mutex);
@@ -455,12 +464,19 @@ wl_proxy_destroy(struct wl_proxy *proxy)
  * \c n, \c implementation[n] should point to the handler of \c n for
  * the given object.
  *
+ * \c proxy must not be a proxy wrapper.
+ *
  * \memberof wl_proxy
  */
 WL_EXPORT int
 wl_proxy_add_listener(struct wl_proxy *proxy,
 		      void (**implementation)(void), void *data)
 {
+	if (proxy->flags == WL_PROXY_FLAG_WRAPPER) {
+		wl_log("proxy %p is a wrapper\n", proxy);
+		return -1;
+	}
+
 	if (proxy->object.implementation || proxy->dispatcher) {
 		wl_log("proxy %p already has listener\n", proxy);
 		return -1;
@@ -507,6 +523,8 @@ wl_proxy_get_listener(struct wl_proxy *proxy)
  * The exact details of dispatcher_data depend on the dispatcher used.  This
  * function is intended to be used by language bindings, not user code.
  *
+ * \c proxy must not be a proxy wrapper.
+ *
  * \memberof wl_proxy
  */
 WL_EXPORT int
@@ -514,6 +532,11 @@ wl_proxy_add_dispatcher(struct wl_proxy *proxy,
 			wl_dispatcher_func_t dispatcher,
 			const void *implementation, void *data)
 {
+	if (proxy->flags == WL_PROXY_FLAG_WRAPPER) {
+		wl_log("proxy %p is a wrapper\n", proxy);
+		return -1;
+	}
+
 	if (proxy->object.implementation || proxy->dispatcher) {
 		wl_log("proxy %p already has listener\n", proxy);
 		return -1;
@@ -1955,6 +1978,116 @@ wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue)
 		proxy->queue = &proxy->display->default_queue;
 }
 
+/** Create a proxy wrapper
+ *
+ * \param proxy The proxy object to be wrapped
+ * \return A proxy wrapper for the given proxy
+ *
+ * A proxy wrapper is type of 'struct wl_proxy' instance that can be used when
+ * sending requests instead of using the original proxy. A proxy wrapper does
+ * not have an implementation or dispatcher, and events received on the
+ * object is still emitted on the original proxy. Trying to set an
+ * implementation or dispatcher will have no effect but result in a warning
+ * being logged.
+ *
+ * Setting the proxy queue of the proxy wrapper will make new objects created
+ * using the proxy wrapper use the set proxy queue.
+ * Even though there is no implementation nor dispatcher, the proxy queue can
+ * be changed. This will affect the default queue of new objects created by
+ * requests sent via the proxy wrapper.
+ *
+ * A proxy wrapper can only be destroyed using wl_proxy_wrapper_destroy().
+ *
+ * A proxy wrapper must be destroyed using before the proxy it was created from.
+ * Creating a proxy wrapper on a proxy that has either been destroyed or
+ * removed will fail, and NULL will be returned.
+ *
+ * If a user reads and dispatches events on more than one thread, it is
+ * necessary to use a proxy wrapper when sending requests on objects when the
+ * intention is that a newly created proxy is to use a proxy queue different
+ * from the proxy the request was sent on, as creating the new proxy and then
+ * setting the queue is not thread safe.
+ *
+ * For example, a module that runs using its own proxy queue that needs to
+ * do display roundtrip must wrap the wl_display proxy object before sending
+ * the wl_display.sync request. For example:
+ *
+ * \code
+ *
+ *   struct wl_event_queue *queue = ...;
+ *   struct wl_display *wrapped_display;
+ *   struct wl_callback *callback;
+ *
+ *   wrapped_display = wl_proxy_create_wrapper(display);
+ *   wl_proxy_set_queue((struct wl_proxy *) wrapped_display, queue);
+ *   callback = wl_display_sync(wrapped_display);
+ *   wl_proxy_wrapper_destroy(wrapped_display);
+ *   wl_callback_add_listener(callback, ...);
+ *
+ * \endcode
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT void *
+wl_proxy_create_wrapper(void *proxy)
+{
+	struct wl_proxy *wrapped_proxy = proxy;
+	struct wl_proxy *wrapper;
+
+	if (wrapped_proxy->flags == WL_PROXY_FLAG_WRAPPER) {
+		wl_log("tried to wrap proxy wrapper\n");
+		return NULL;
+	}
+
+	wrapper = zalloc(sizeof *wrapper);
+	if (!wrapper)
+		return NULL;
+
+	pthread_mutex_lock(&wrapped_proxy->display->mutex);
+
+	if ((wrapped_proxy->flags & WL_PROXY_FLAG_ID_DELETED) ||
+	    (wrapped_proxy->flags & WL_PROXY_FLAG_DESTROYED)) {
+		pthread_mutex_unlock(&wrapped_proxy->display->mutex);
+		wl_log("proxy %p already deleted or destroyed flag: 0x%x\n",
+		       wrapped_proxy, wrapped_proxy->flags);
+		free(wrapper);
+		return NULL;
+	}
+
+	wrapper->object.interface = wrapped_proxy->object.interface;
+	wrapper->object.id = wrapped_proxy->object.id;
+	wrapper->version = wrapped_proxy->version;
+	wrapper->display = wrapped_proxy->display;
+	wrapper->queue = wrapped_proxy->queue;
+	wrapper->flags = WL_PROXY_FLAG_WRAPPER;
+	wrapper->refcount = 1;
+
+	pthread_mutex_unlock(&wrapped_proxy->display->mutex);
+
+	return wrapper;
+}
+
+/** Destroy a proxy wrapper
+ * \param proxy_wrapper The proxy wrapper to be destroyed
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT void
+wl_proxy_wrapper_destroy(void *proxy_wrapper)
+{
+	struct wl_proxy *wrapper = proxy_wrapper;
+
+	if (wrapper->flags != WL_PROXY_FLAG_WRAPPER) {
+		wl_log("warning: tried to destroy non-wrapper proxy with "
+		       "wl_proxy_wrapper_destroy\n");
+		return;
+	}
+
+	assert(wrapper->refcount == 1);
+
+	free(wrapper);
+}
+
 WL_EXPORT void
 wl_log_set_handler_client(wl_log_func_t handler)
 {
-- 
2.5.5



More information about the wayland-devel mailing list