client: extend error handling

When an error occurs, wl_display_get_error() does not
provide any way of getting know if it was a local error or if it was
an error event, respectively what object caused the error and what
the error was.

This patch introduces a new function wl_display_get_protocol_error()
which will return error code, interface and id of the object that
generated the error.
wl_display_get_error() will work the same way as before.

wl_display_get_protocol_error() DOES NOT indicate that a non-protocol
error happened. It returns valid information only in that case that
(protocol) error occurred, so it should be used after calling
wl_display_get_error() with positive result.

[Pekka Paalanen] Applied another hunk of Bryce's comments to docs,
	added libtool version bump.

Reviewed-by: Pekka Paalanen <ppaalanen@gmail.com>
Reviewed-by: Bryce Harrington <b.harrington@samsung.com>
This commit is contained in:
Marek Chalupa 2014-06-20 09:29:52 +02:00 committed by Pekka Paalanen
parent 567977815a
commit 886b09c9a3
3 changed files with 123 additions and 19 deletions

View File

@ -51,7 +51,7 @@ nodist_libwayland_server_la_SOURCES = \
libwayland_client_la_CFLAGS = $(FFI_CFLAGS) $(GCC_CFLAGS) -pthread
libwayland_client_la_LIBADD = $(FFI_LIBS) libwayland-util.la -lrt -lm
libwayland_client_la_LDFLAGS = -version-info 2:0:2
libwayland_client_la_LDFLAGS = -version-info 3:0:3
libwayland_client_la_SOURCES = \
src/wayland-client.c

View File

@ -78,7 +78,24 @@ struct wl_event_queue {
struct wl_display {
struct wl_proxy proxy;
struct wl_connection *connection;
/* errno of the last wl_display error */
int last_error;
/* When display gets an error event from some object, it stores
* information about it here, so that client can get this
* information afterwards */
struct {
/* Code of the error. It can be compared to
* the interface's errors enumeration. */
uint32_t code;
/* interface (protocol) in which the error occurred */
const struct wl_interface *interface;
/* id of the proxy that caused the error. There's no warranty
* that the proxy is still valid. It's up to client how it will
* use it */
uint32_t id;
} protocol_error;
int fd;
pthread_t display_thread;
struct wl_map objects;
@ -96,6 +113,14 @@ struct wl_display {
static int debug_client = 0;
/**
* This function is called for local errors (no memory, server hung up)
*
* \param display
* \param error error value (EINVAL, EFAULT, ...)
*
* \note this function is called with display mutex locked
*/
static void
display_fatal_error(struct wl_display *display, int error)
{
@ -105,7 +130,7 @@ display_fatal_error(struct wl_display *display, int error)
return;
if (!error)
error = 1;
error = EFAULT;
display->last_error = error;
@ -113,11 +138,56 @@ display_fatal_error(struct wl_display *display, int error)
pthread_cond_broadcast(&iter->cond);
}
/**
* This function is called for error events
* and indicates that in some object an error occured.
* Difference between this function and display_fatal_error()
* is that this one handles errors that will come by wire,
* whereas display_fatal_error() is called for local errors.
*
* \param display
* \param code error code
* \param id id of the object that generated the error
* \param intf protocol interface
*/
static void
wl_display_fatal_error(struct wl_display *display, int error)
display_protocol_error(struct wl_display *display, uint32_t code,
uint32_t id, const struct wl_interface *intf)
{
struct wl_event_queue *iter;
int err;
if (display->last_error)
return;
/* set correct errno */
if (wl_interface_equal(intf, &wl_display_interface)) {
switch (code) {
case WL_DISPLAY_ERROR_INVALID_OBJECT:
case WL_DISPLAY_ERROR_INVALID_METHOD:
err = EINVAL;
break;
case WL_DISPLAY_ERROR_NO_MEMORY:
err = ENOMEM;
break;
default:
err = EFAULT;
}
} else {
err = EPROTO;
}
pthread_mutex_lock(&display->mutex);
display_fatal_error(display, error);
display->last_error = err;
display->protocol_error.code = code;
display->protocol_error.id = id;
display->protocol_error.interface = intf;
wl_list_for_each(iter, &display->event_queue_list, link)
pthread_cond_broadcast(&iter->cond);
pthread_mutex_unlock(&display->mutex);
}
@ -579,25 +649,12 @@ display_handle_error(void *data,
uint32_t code, const char *message)
{
struct wl_proxy *proxy = object;
int err;
wl_log("%s@%u: error %d: %s\n",
proxy->object.interface->name, proxy->object.id, code, message);
switch (code) {
case WL_DISPLAY_ERROR_INVALID_OBJECT:
case WL_DISPLAY_ERROR_INVALID_METHOD:
err = EINVAL;
break;
case WL_DISPLAY_ERROR_NO_MEMORY:
err = ENOMEM;
break;
default:
err = EFAULT;
break;
}
wl_display_fatal_error(display, err);
display_protocol_error(display, code, proxy->object.id,
proxy->object.interface);
}
static void
@ -1486,6 +1543,50 @@ wl_display_get_error(struct wl_display *display)
return ret;
}
/**
* Retrieves the information about a protocol error:
*
* \param display The Wayland display
* \param interface if not NULL, stores the interface where the error occurred
* \param id if not NULL, stores the object id that generated
* the error. There's no guarantee the object is
* still valid; the client must know if it deleted the object.
* \return The error code as defined in the interface specification.
*
* \code
* int err = wl_display_get_error(display);
*
* if (err == EPROTO) {
* code = wl_display_get_protocol_error(display, &interface, &id);
* handle_error(code, interface, id);
* }
*
* ...
*
* \endcode
*/
WL_EXPORT uint32_t
wl_display_get_protocol_error(struct wl_display *display,
const struct wl_interface **interface,
uint32_t *id)
{
uint32_t ret;
pthread_mutex_lock(&display->mutex);
ret = display->protocol_error.code;
if (interface)
*interface = display->protocol_error.interface;
if (id)
*id = display->protocol_error.id;
pthread_mutex_unlock(&display->mutex);
return ret;
}
/** Send all buffered requests on the display to the server
*
* \param display The display context object

View File

@ -161,6 +161,9 @@ int wl_display_dispatch_queue_pending(struct wl_display *display,
struct wl_event_queue *queue);
int wl_display_dispatch_pending(struct wl_display *display);
int wl_display_get_error(struct wl_display *display);
uint32_t wl_display_get_protocol_error(struct wl_display *display,
const struct wl_interface **interface,
uint32_t *id);
int wl_display_flush(struct wl_display *display);
int wl_display_roundtrip(struct wl_display *display);