If any callback or helper function fails with a fatal error, we now
set the last_error flag and prevent all further I/O on the wl_display. We
wake up all sleeping event-queues and notify the caller that they
should shutdown wl_display.

Signed-off-by: David Herrmann <[email protected]>
---
 src/wayland-client.c | 106 +++++++++++++++++++++++++++++++++++++++++----------
 src/wayland-client.h |   2 +-
 2 files changed, 87 insertions(+), 21 deletions(-)

diff --git a/src/wayland-client.c b/src/wayland-client.c
index cd2f508..4e8f85a 100644
--- a/src/wayland-client.c
+++ b/src/wayland-client.c
@@ -79,6 +79,33 @@ struct wl_display {
 static int wl_debug = 0;
 
 static void
+display_fatal_error(struct wl_display *display, int error)
+{
+       struct wl_event_queue *iter;
+
+       if (display->last_error)
+               return;
+
+       if (!error)
+               error = 1;
+
+       display->last_error = error;
+       close(display->fd);
+       display->fd = -1;
+
+       wl_list_for_each(iter, &display->event_queues, link)
+               pthread_cond_broadcast(&iter->cond);
+}
+
+static void
+wl_display_fatal_error(struct wl_display *display, int error)
+{
+       pthread_mutex_lock(&display->mutex);
+       display_fatal_error(display, error);
+       pthread_mutex_unlock(&display->mutex);
+}
+
+static void
 wl_event_queue_init(struct wl_event_queue *queue, struct wl_display *display)
 {
        wl_list_init(&queue->event_list);
@@ -266,9 +293,7 @@ display_handle_error(void *data,
                break;
        }
 
-       pthread_mutex_lock(&display->mutex);
-       display->last_error = err;
-       pthread_mutex_unlock(&display->mutex);
+       wl_display_fatal_error(display, err);
 }
 
 static void
@@ -430,14 +455,21 @@ wl_display_disconnect(struct wl_display *display)
        wl_connection_destroy(display->connection);
        wl_map_release(&display->objects);
        wl_event_queue_release(&display->queue);
-       close(display->fd);
+       if (display->fd >= 0)
+               close(display->fd);
        free(display);
 }
 
 WL_EXPORT int
 wl_display_get_fd(struct wl_display *display)
 {
-       return display->fd;
+       int fd;
+
+       pthread_mutex_lock(&display->mutex);
+       fd = display->fd;
+       pthread_mutex_unlock(&display->mutex);
+
+       return fd;
 }
 
 static void
@@ -453,17 +485,19 @@ static const struct wl_callback_listener sync_listener = {
        sync_callback
 };
 
-WL_EXPORT void
+WL_EXPORT int
 wl_display_roundtrip(struct wl_display *display)
 {
        struct wl_callback *callback;
-       int done;
+       int done, ret = 0;
 
        done = 0;
        callback = wl_display_sync(display);
        wl_callback_add_listener(callback, &sync_listener, &done);
-       while (!done)
-               wl_display_dispatch(display);
+       while (!done && !ret)
+               ret = wl_display_dispatch(display);
+
+       return ret;
 }
 
 static int
@@ -529,10 +563,12 @@ queue_event(struct wl_display *display, int len)
        message = &proxy->object.interface->events[opcode];
        closure = wl_connection_demarshal(display->connection, size,
                                          &display->objects, message);
+       if (!closure)
+               return -1;
 
-       if (closure == NULL || create_proxies(proxy, closure) < 0) {
-               fprintf(stderr, "Error demarshalling event\n");
-               abort();
+       if (create_proxies(proxy, closure) < 0) {
+               wl_closure_destroy(closure);
+               return -1;
        }
 
        if (wl_list_empty(&proxy->queue->event_list))
@@ -585,36 +621,59 @@ WL_EXPORT int
 wl_display_dispatch_queue(struct wl_display *display,
                          struct wl_event_queue *queue)
 {
-       int len, size;
+       int len, size, ret;
 
        pthread_mutex_lock(&display->mutex);
 
-       /* FIXME: Handle flush errors, EAGAIN... */
-       wl_connection_flush(display->connection);
+       if (display->last_error)
+               goto err_unlock;
+
+       ret = wl_connection_flush(display->connection);
+       if (ret < 0 && errno != EAGAIN) {
+               display_fatal_error(display, errno);
+               goto err_unlock;
+       }
 
        if (wl_list_empty(&queue->event_list) &&
            pthread_equal(display->display_thread, pthread_self())) {
                len = wl_connection_read(display->connection);
                if (len == -1) {
-                       pthread_mutex_unlock(&display->mutex);
-                       return -1;
+                       display_fatal_error(display, errno);
+                       goto err_unlock;
+               } else if (len == 0) {
+                       display_fatal_error(display, EPIPE);
+                       goto err_unlock;
                }
                while (len >= 8) {
                        size = queue_event(display, len);
-                       if (size == 0)
+                       if (size == -1) {
+                               display_fatal_error(display, errno);
+                               goto err_unlock;
+                       } else if (size == 0) {
                                break;
+                       }
                        len -= size;
                }
        } else if (wl_list_empty(&queue->event_list)) {
                pthread_cond_wait(&queue->cond, &display->mutex);
+               if (display->last_error)
+                       goto err_unlock;
        }
 
-       while (!wl_list_empty(&queue->event_list))
+       while (!wl_list_empty(&queue->event_list)) {
                dispatch_event(display, queue);
+               if (display->last_error)
+                       goto err_unlock;
+       }
 
        pthread_mutex_unlock(&display->mutex);
 
        return 0;
+
+err_unlock:
+       errno = display->last_error;
+       pthread_mutex_unlock(&display->mutex);
+       return -1;
 }
 
 WL_EXPORT int
@@ -646,7 +705,14 @@ wl_display_flush(struct wl_display *display)
 
        pthread_mutex_lock(&display->mutex);
 
-       ret = wl_connection_flush(display->connection);
+       if (display->last_error) {
+               errno = display->last_error;
+               ret = -1;
+       } else {
+               ret = wl_connection_flush(display->connection);
+               if (ret < 0 && errno != EAGAIN)
+                       display_fatal_error(display, errno);
+       }
 
        pthread_mutex_unlock(&display->mutex);
 
diff --git a/src/wayland-client.h b/src/wayland-client.h
index 6fae273..637094c 100644
--- a/src/wayland-client.h
+++ b/src/wayland-client.h
@@ -63,7 +63,7 @@ int wl_display_dispatch_queue(struct wl_display *display,
 int wl_display_get_error(struct wl_display *display);
 
 int wl_display_flush(struct wl_display *display);
-void wl_display_roundtrip(struct wl_display *display);
+int wl_display_roundtrip(struct wl_display *display);
 struct wl_event_queue *wl_display_create_queue(struct wl_display *display);
 
 void wl_log_set_handler_client(wl_log_func_t handler);
-- 
1.7.12.2

_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to