wayland-client: forward fatal errors to caller

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 <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2012-10-11 23:37:53 +02:00 committed by Kristian Høgsberg
parent edae4ffa37
commit 33b7637b45
2 changed files with 81 additions and 20 deletions

View file

@ -83,6 +83,33 @@ struct wl_display {
static int wl_debug = 0; 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_queue_list, 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 static void
wl_event_queue_init(struct wl_event_queue *queue, struct wl_display *display) wl_event_queue_init(struct wl_event_queue *queue, struct wl_display *display)
{ {
@ -358,9 +385,7 @@ display_handle_error(void *data,
break; break;
} }
pthread_mutex_lock(&display->mutex); wl_display_fatal_error(display, err);
display->last_error = err;
pthread_mutex_unlock(&display->mutex);
} }
static void static void
@ -549,7 +574,8 @@ wl_display_disconnect(struct wl_display *display)
wl_map_release(&display->objects); wl_map_release(&display->objects);
wl_event_queue_release(&display->queue); wl_event_queue_release(&display->queue);
pthread_mutex_destroy(&display->mutex); pthread_mutex_destroy(&display->mutex);
close(display->fd); if (display->fd > 0)
close(display->fd);
free(display); free(display);
} }
@ -592,17 +618,19 @@ static const struct wl_callback_listener sync_listener = {
* *
* \memberof wl_display * \memberof wl_display
*/ */
WL_EXPORT void WL_EXPORT int
wl_display_roundtrip(struct wl_display *display) wl_display_roundtrip(struct wl_display *display)
{ {
struct wl_callback *callback; struct wl_callback *callback;
int done; int done, ret = 0;
done = 0; done = 0;
callback = wl_display_sync(display); callback = wl_display_sync(display);
wl_callback_add_listener(callback, &sync_listener, &done); wl_callback_add_listener(callback, &sync_listener, &done);
while (!done) while (!done && !ret)
wl_display_dispatch(display); ret = wl_display_dispatch(display);
return ret;
} }
static int static int
@ -668,10 +696,12 @@ queue_event(struct wl_display *display, int len)
message = &proxy->object.interface->events[opcode]; message = &proxy->object.interface->events[opcode];
closure = wl_connection_demarshal(display->connection, size, closure = wl_connection_demarshal(display->connection, size,
&display->objects, message); &display->objects, message);
if (!closure)
return -1;
if (closure == NULL || create_proxies(proxy, closure) < 0) { if (create_proxies(proxy, closure) < 0) {
fprintf(stderr, "Error demarshalling event\n"); wl_closure_destroy(closure);
abort(); return -1;
} }
if (wl_list_empty(&proxy->queue->event_list)) if (wl_list_empty(&proxy->queue->event_list))
@ -724,36 +754,60 @@ static int
dispatch_queue(struct wl_display *display, dispatch_queue(struct wl_display *display,
struct wl_event_queue *queue, int block) struct wl_event_queue *queue, int block)
{ {
int len, size, count; int len, size, count, ret;
pthread_mutex_lock(&display->mutex); pthread_mutex_lock(&display->mutex);
/* FIXME: Handle flush errors, EAGAIN... */ if (display->last_error)
wl_connection_flush(display->connection); goto err_unlock;
ret = wl_connection_flush(display->connection);
if (ret < 0 && errno != EAGAIN) {
display_fatal_error(display, errno);
goto err_unlock;
}
if (block && wl_list_empty(&queue->event_list) && if (block && wl_list_empty(&queue->event_list) &&
pthread_equal(display->display_thread, pthread_self())) { pthread_equal(display->display_thread, pthread_self())) {
len = wl_connection_read(display->connection); len = wl_connection_read(display->connection);
if (len == -1) { if (len == -1) {
pthread_mutex_unlock(&display->mutex); display_fatal_error(display, errno);
return -1; goto err_unlock;
} else if (len == 0) {
display_fatal_error(display, EPIPE);
goto err_unlock;
} }
while (len >= 8) { while (len >= 8) {
size = queue_event(display, len); size = queue_event(display, len);
if (size == 0) if (size == -1) {
display_fatal_error(display, errno);
goto err_unlock;
} else if (size == 0) {
break; break;
}
len -= size; len -= size;
} }
} else if (block && wl_list_empty(&queue->event_list)) { } else if (block && wl_list_empty(&queue->event_list)) {
pthread_cond_wait(&queue->cond, &display->mutex); pthread_cond_wait(&queue->cond, &display->mutex);
if (display->last_error)
goto err_unlock;
} }
for (count = 0; !wl_list_empty(&queue->event_list); count++) for (count = 0; !wl_list_empty(&queue->event_list); count++) {
dispatch_event(display, queue); dispatch_event(display, queue);
if (display->last_error)
goto err_unlock;
}
pthread_mutex_unlock(&display->mutex); pthread_mutex_unlock(&display->mutex);
return count; return count;
err_unlock:
errno = display->last_error;
pthread_mutex_unlock(&display->mutex);
return -1;
} }
/** Dispatch events in an event queue /** Dispatch events in an event queue
@ -834,7 +888,14 @@ wl_display_flush(struct wl_display *display)
pthread_mutex_lock(&display->mutex); 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); pthread_mutex_unlock(&display->mutex);

View file

@ -121,7 +121,7 @@ int wl_display_dispatch_pending(struct wl_display *display);
int wl_display_get_error(struct wl_display *display); int wl_display_get_error(struct wl_display *display);
int wl_display_flush(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); struct wl_event_queue *wl_display_create_queue(struct wl_display *display);
void wl_log_set_handler_client(wl_log_func_t handler); void wl_log_set_handler_client(wl_log_func_t handler);