[PATCH 4/8] client: Add wl_event_queue for multi-thread dispatching

Ander Conselvan de Oliveira conselvan2 at gmail.com
Wed Oct 10 05:43:26 PDT 2012


On 10/10/2012 05:38 AM, Kristian Høgsberg wrote:
> This introduces wl_event_queue, which is what will make multi-threaded
> wayland clients possible and useful.  The driving use case is that of a
> GL rendering thread that renders and calls eglSwapBuffer independently of
> a "main thread" that owns the wl_display and handles input events and
> everything else.  In general, the EGL and GL APIs have a threading model
> that requires the wayland client library to be usable from several threads.
> Finally, the current callback model gets into trouble even in a single
> threaded scenario: if we have to block in eglSwapBuffers, we may end up
> doing unrelated callbacks from within EGL.
>
> The wl_event_queue mechanism lets the application (or middleware such as
> EGL or toolkits) assign a proxy to an event queue.  Only events from objects
> associated with the queue will be put in the queue, and conversely,
> events from objects associated with the queue will not be queue up anywhere
> else.  The wl_display struct has a built-in event queue, which is considered
> the main and default event queue.  New proxies are associated with the
> same queue as the object that created them (either the object that a
> request with a new-id argument was sent to or the object that sent an
> event with a new-id argument).  A proxy can be moved to a different event
> queue by calling wl_proxy_set_queue().
>
> A subsystem, such as EGL, will then create its own event queue and associate
> the objects it expects to receive events from with that queue.  If EGL
> needs to block and wait for a certain event, it can keep dispatching event
> from its queue until that events comes in.  This wont call out to unrelated
> code with an EGL lock held.  Similarly, we don't risk the main thread
> handling an event from an EGL object and then calling into EGL from a
> different thread without the lock held.
> ---
>   src/wayland-client.c |  148 ++++++++++++++++++++++++++++++++++++++------------
>   src/wayland-client.h |    8 +++
>   2 files changed, 122 insertions(+), 34 deletions(-)
>
> diff --git a/src/wayland-client.c b/src/wayland-client.c
> index 6a499e0..694fd39 100644
> --- a/src/wayland-client.c
> +++ b/src/wayland-client.c

[...]

> @@ -552,45 +612,50 @@ queue_event(struct wl_display *display, int len, struct wl_list *list)
>   	if (wl_debug)
>   		wl_closure_print(closure, &proxy->object, false);
>
> -	if (closure == NULL || create_proxies(display, closure) < 0) {
> +	if (closure == NULL || create_proxies(proxy, closure) < 0) {
>   		fprintf(stderr, "Error demarshalling event\n");
>   		abort();
>   	}
>
> -	wl_list_insert(list->prev, &closure->link);
> +	if (wl_list_empty(&proxy->queue->event_list))
> +		pthread_cond_signal(&proxy->queue->cond);
> +	wl_list_insert(proxy->queue->event_list.prev, &closure->link);

I find a bit confusing that event is added to the queue after signaling 
the condition although I see this works because the other thread will 
need to get the display lock before proceeding and this is held here. 
Maybe a comment would help.

[...]

> @@ -599,27 +664,35 @@ wl_display_dispatch(struct wl_display *display)
>   	/* FIXME: Handle flush errors, EAGAIN... */
>   	wl_connection_flush(display->connection);
>
> -	/* FIXME: Shouldn't always read here... */
> -	len = wl_connection_read(display->connection);
> -	if (len == -1)
> -		return -1;
> -
> -	wl_list_init(&list);
> -	while (len >= 8) {
> -		size = queue_event(display, len, &list);
> -		if (size == 0)
> -			break;
> -		len -= size;
> +	if (wl_list_empty(&queue->event_list) &&
> +	    pthread_equal(display->display_thread, pthread_self())) {
> +		len = wl_connection_read(display->connection);
> +		if (len == -1)
> +			return -1;

This returns with the display lock held.

Cheers,
Ander

> +		while (len >= 8) {
> +			size = queue_event(display, len);
> +			if (size == 0)
> +				break;
> +			len -= size;
> +		}
> +	} else if (wl_list_empty(&queue->event_list)) {
> +		pthread_cond_wait(&queue->cond, &display->mutex);
>   	}
>
> +	while (!wl_list_empty(&queue->event_list))
> +		dispatch_event(display, queue);
> +
>   	pthread_mutex_unlock(&display->mutex);
>
> -	while (!wl_list_empty(&list)) {
> -		closure = container_of(list.next, struct wl_closure, link);
> -		dispatch_event(display, closure);
> -	}
> +	return 0;
> +}
> +
> +WL_EXPORT int
> +wl_display_dispatch(struct wl_display *display)
> +{
> +	display->display_thread = pthread_self();
>
> -	return len;
> +	return wl_display_dispatch_queue(display, &display->queue);
>   }
>
>   WL_EXPORT int
> @@ -685,6 +758,13 @@ wl_proxy_get_id(struct wl_proxy *proxy)
>   	return proxy->object.id;
>   }
>
> +
> +WL_EXPORT void
> +wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue)
> +{
> +	proxy->queue = queue;
> +}
> +
>   WL_EXPORT void
>   wl_log_set_handler_client(wl_log_func_t handler)
>   {
> diff --git a/src/wayland-client.h b/src/wayland-client.h
> index 5fcb86d..fbbee09 100644
> --- a/src/wayland-client.h
> +++ b/src/wayland-client.h
> @@ -32,6 +32,9 @@ extern "C" {
>
>   struct wl_proxy;
>   struct wl_display;
> +struct wl_event_queue;
> +
> +void wl_event_queue_destroy(struct wl_event_queue *queue);
>
>   void wl_proxy_marshal(struct wl_proxy *p, uint32_t opcode, ...);
>   struct wl_proxy *wl_proxy_create(struct wl_proxy *factory,
> @@ -46,6 +49,7 @@ int wl_proxy_add_listener(struct wl_proxy *proxy,
>   void wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data);
>   void *wl_proxy_get_user_data(struct wl_proxy *proxy);
>   uint32_t wl_proxy_get_id(struct wl_proxy *proxy);
> +void wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue);
>
>   void *wl_display_bind(struct wl_display *display,
>   		      uint32_t name, const struct wl_interface *interface);
> @@ -74,8 +78,12 @@ struct wl_display *wl_display_connect_to_fd(int fd);
>   void wl_display_disconnect(struct wl_display *display);
>   int wl_display_get_fd(struct wl_display *display);
>   int wl_display_dispatch(struct wl_display *display);
> +int wl_display_dispatch_queue(struct wl_display *display,
> +			      struct wl_event_queue *queue);
> +
>   int wl_display_flush(struct wl_display *display);
>   void wl_display_roundtrip(struct wl_display *display);
> +struct wl_event_queue *wl_display_create_queue(struct wl_display *display);
>
>   struct wl_global_listener;
>   typedef void (*wl_display_global_func_t)(struct wl_display *display,
>



More information about the wayland-devel mailing list