[PATCH 1/3] client: Send errors should be fatal but not abort
Pekka Paalanen
ppaalanen at gmail.com
Tue Jan 22 12:31:55 UTC 2019
On Fri, 7 Sep 2018 18:14:03 -0700
Lloyd Pique <lpique at google.com> wrote:
> If the core-client code encounters an error when doing an internal flush
> because the send buffers are full, the previous behavior is to
> wl_abort(). This is not very friendly if the client is a nested server
> running as a service.
>
> This patch instead sets a fatal display error, which is one step
> better, and causes the send logic to do nothing once a fatal display
> error is set. The client should eventually fall back to its main loop,
> and be able to detect the fatal error there.
>
> The existing display test is extended to exercise the error state.
>
> Signed-off-by: Lloyd Pique <lpique at google.com>
> ---
> COPYING | 1 +
> src/wayland-client.c | 9 +++++-
> tests/display-test.c | 77 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 86 insertions(+), 1 deletion(-)
>
> diff --git a/COPYING b/COPYING
> index eb25a4e..bace5d7 100644
> --- a/COPYING
> +++ b/COPYING
> @@ -2,6 +2,7 @@ Copyright © 2008-2012 Kristian Høgsberg
> Copyright © 2010-2012 Intel Corporation
> Copyright © 2011 Benjamin Franzke
> Copyright © 2012 Collabora, Ltd.
> +Copyright © 2018 Google LLC
Hi Lloyd,
would you like to add this in wayland-client.c as well?
The overall logic looks good, but there are details to discuss below.
>
> Permission is hereby granted, free of charge, to any person obtaining a
> copy of this software and associated documentation files (the "Software"),
> diff --git a/src/wayland-client.c b/src/wayland-client.c
> index 0ccfc66..29ae1e0 100644
> --- a/src/wayland-client.c
> +++ b/src/wayland-client.c
> @@ -736,6 +736,9 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,
> goto err_unlock;
> }
>
> + if (proxy->display->last_error)
> + goto err_unlock;
> +
> closure = wl_closure_marshal(&proxy->object, opcode, args, message);
> if (closure == NULL)
> wl_abort("Error marshalling request: %s\n", strerror(errno));
> @@ -744,7 +747,11 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,
> wl_closure_print(closure, &proxy->object, true);
>
> if (wl_closure_send(closure, proxy->display->connection))
> - wl_abort("Error sending request: %s\n", strerror(errno));
> + // Convert EAGAIN into EPIPE, since EAGAIN is not really
> + // supposed to be fatal, especially when returned by
> + // wl_display_flush().
The style is to use /* */ comments.
> + display_fatal_error(proxy->display,
> + errno != EAGAIN ? errno : EPIPE);
>
> wl_closure_destroy(closure);
The logic in wayland-client.c looks good. I'm just wondering if EPIPE is
a good error code to return. EPIPE can result otherwise as well,
particularly when the server disconnects.
Could we use something else like ENOMEM or ENOSPC that would be more
unique? I see ENOMEM already used, so maybe ENOSPC?
I checked, and libwayland-client does not seem to be checking
last_error against EPIPE or really any other specific value, so I think
the concern is about what the users of the library will do. FWIW,
Weston has no occurrences of EPIPE.
>
> diff --git a/tests/display-test.c b/tests/display-test.c
> index 9b49a0e..23f0635 100644
> --- a/tests/display-test.c
> +++ b/tests/display-test.c
> @@ -1315,3 +1315,80 @@ TEST(zombie_fd_errant_consumption)
>
> display_destroy(d);
> }
> +
> +static void
> +terminate_on_bind_attempt(struct wl_client *client, void *data,
> + uint32_t version, uint32_t id)
> +{
> + wl_display_terminate(((struct display *)data)->wl_display);
> +}
> +
> +static void
> +fill_client_send_buffers(struct wl_display *display,
> + void (*send_check)(struct wl_display *, int *), int *send_check_result)
Align this line with "struct" above and split the last argument to a
new line.
> +{
> + int sync_count = 4096;
> + int socket_buf = 1024;
> +
> + /* Use the minimum send buffer size. We want the client to be
> + * able to fill the buffer easily. */
> + assert(setsockopt(wl_display_get_fd(display), SOL_SOCKET, SO_SNDBUF,
> + &socket_buf, sizeof(socket_buf)) == 0);
One more space of alignment to right.
> +
> + /* Fill up the client and server buffers With the default error
> + * handling, this will abort the client. */
> + while(sync_count--) {
Add space between "while" and "(".
> + wl_callback_destroy(wl_display_sync(display));
This should consume 12 bytes per call IIRC, so it should easily fill up
the 4096+1024 bytes of wl_buffer and socket send buffer. Ok.
> + if (send_check != NULL) {
> + send_check(display, send_check_result);
> + }
> + }
> +
> + wl_display_roundtrip(display);
> +}
> +
> +static void
> +send_error_client(void)
> +{
> + struct client *c = client_connect();
> +
> + /* Try and bind a wl_set, which isn't used but has a side effect. */
typo: "wl_seat"
What side-effect are you relying on?
> + wl_proxy_destroy((struct wl_proxy *)client_get_seat(c));
Why not use wl_seat_destroy()?
> +
> + /* Verify we do not have any errors at this point */
> + assert(wl_display_get_error(c->wl_display) == 0);
> +
> + fill_client_send_buffers(c->wl_display, NULL, NULL);
> +
> + /* Verify that we see a fatal display error from the send. */
> + assert(wl_display_get_error(c->wl_display) == EPIPE);
> +
> + client_disconnect_nocheck(c);
Looks good.
> +}
> +
> +TEST(client_send_error_tst)
How about "client_send_flood_error" or such?
> +{
> + struct display *d = display_create();
> +
> + test_set_timeout(2);
> +
> + client_create_noarg(d, send_error_client);
> +
> + /* The client must connect before simulating an unresponsive state. Use
> + * the request for a set to terminate the event loop. */
Is it a typo "seat"?
> + wl_global_create(d->wl_display, &wl_seat_interface,
> + 1, d, terminate_on_bind_attempt);
> +
> + /* This handles the initial connect, then returns. */
> + display_run(d);
> +
> + /* Not processing any events for a moment should allow the buffers
> + * to be filled up by the client, and it to enter an error state then
> + * disconnect. */
> + test_sleep(1);
> +
> + /* We need one last dispatch to handle the terminated client. */
> + wl_event_loop_dispatch(wl_display_get_event_loop(d->wl_display), 0);
> +
> + display_destroy(d);
> +}
Yeah, this logic looks correct. I tried to think how to use the "stop
display" mechanism, but that does not seem to fit because you need the
server to stall while the client continues.
I don't like sleeps much, I'd prefer to wait until the client process
exits, but I don't see a good way to implement that. Waiting on the pid
would probably make handle_client_destroy() fail unexpectedly.
Thanks,
pq
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <https://lists.freedesktop.org/archives/wayland-devel/attachments/20190122/745a1600/attachment.sig>
More information about the wayland-devel
mailing list