<div dir="ltr"><div>Hi Pekka!</div><div><br></div><div>Thank you for the feedback. In the end, I think we have a good basic agreement about the patch, and <span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">after reading your linked bug (</span><a href="https://gitlab.freedesktop.org/wayland/wayland/issues/12" rel="noreferrer" target="_blank" style="color:rgb(17,85,204);background-color:rgb(255,255,255)">https://gitlab.freedesktop.org/wayland/wayland/issues/12</a><span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">), which I was previously unaware of, it sounds like I will be just stripping out the callback+retry case from my patch.</span></div><div><span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><br></span></div><div><span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">I will probably go ahead and add a function to check for the half-full output buffer, and one you didn't think of to check whether MAX_FDS_OUT is about to be exceeded by the next call (that count is small enough to actually be a concern).</span></div><div><br></div><div>At this point I think the only real question I have is one of checking the intent behind the bug you filed. Should the design be that  ONLY behavior is to set a fatal error on the display context, or should there be a public API to select between the previous/default abort and the detal error?</div><div><br></div><div>I'll send out a revised patch after hearing back with that bit of guidance.</div><div><br></div><div>In case it matters, I did also include my responses to your points inline below. I don't expect to convince that this patch should be used as is, but perhaps it will give you or someone else some clarify and insight into why I suggested the change I did.</div><div><br></div><div>Thanks!</div><div><br></div><div>- Lloyd</div><div><div style="text-decoration-style:initial;text-decoration-color:initial"><br></div></div><div class="gmail_quote"><div dir="ltr">On Mon, Jun 18, 2018 at 4:58 AM Pekka Paalanen <<a href="mailto:ppaalanen@gmail.com">ppaalanen@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Tue,  5 Jun 2018 18:14:54 -0700<br>
Lloyd Pique <<a href="mailto:lpique@google.com" target="_blank">lpique@google.com</a>> wrote:<br>
<br>
> Introduce a new call wl_display_set_error_handler_client(), which allows<br>
> a client to register a callback function which is invoked if there is an<br>
> error (possibly transient) while sending messages to the server.<br>
> <br>
> This allows a Wayland client that is actually a nested server to try and<br>
> recover more gracefully from send errors, allowing it one to disconnect<br>
> and reconnect to the server if necessary.<br>
> <br>
> The handler is called with the wl_display and the errno, and is expected<br>
> to return one of WL_SEND_ERROR_ABORT, WL_SEND_ERROR_FATAL, or<br>
> WL_SEND_ERROR_RETRY. The core-client code will then respectively abort()<br>
> the process (the default if no handler is set), set the display context<br>
> into an error state, or retry the send operation that failed.7<br>
> <br>
> The existing display test is extended to exercise the new function.<br>
<br>
Hi Lloyd,<br>
<br>
technically this looks like a quality submission, thank you for that. I<br>
am particularly happy to see all the new cases are added into the test<br>
suite. I agree that calling wl_abort() is nasty and it would be good to<br>
have something else.<br>
<br>
However, I'm not quite convinced of the introduced complexity here.<br>
More comments inline.<br>
<br>
> Signed-off-by: Lloyd Pique <<a href="mailto:lpique@google.com" target="_blank">lpique@google.com</a>><br>
> ---<br>
>  COPYING                   |   1 +<br>
>  src/wayland-client-core.h |  75 +++++++++++++++++<br>
>  src/wayland-client.c      |  67 +++++++++++++++-<br>
>  tests/display-test.c      | 165 +++++++++++++++++++++++++++++++++++++-<br>
>  4 files changed, 306 insertions(+), 2 deletions(-)<br>
> <br>
> diff --git a/COPYING b/COPYING<br>
> index eb25a4e..bace5d7 100644<br>
> --- a/COPYING<br>
> +++ b/COPYING<br>
> @@ -2,6 +2,7 @@ Copyright © 2008-2012 Kristian Høgsberg<br>
>  Copyright © 2010-2012 Intel Corporation<br>
>  Copyright © 2011 Benjamin Franzke<br>
>  Copyright © 2012 Collabora, Ltd.<br>
> +Copyright © 2018 Google LLC<br>
>  <br>
>  Permission is hereby granted, free of charge, to any person obtaining a<br>
>  copy of this software and associated documentation files (the "Software"),<br>
> diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h<br>
> index 03e781b..a3e4b8e 100644<br>
> --- a/src/wayland-client-core.h<br>
> +++ b/src/wayland-client-core.h<br>
> @@ -257,6 +257,81 @@ wl_display_cancel_read(struct wl_display *display);<br>
>  int<br>
>  wl_display_read_events(struct wl_display *display);<br>
>  <br>
> +/** \enum wl_send_error_strategy<br>
> + *<br>
> + * This enum are the valid values that can be returned from a<br>
> + * wl_send_error_handler_func_t<br>
> + *<br>
> + * \sa wl_send_error_handler_func_t<br>
> + */<br>
> +enum wl_send_error_strategy {<br>
> +     /** Abort the client */<br>
> +     WL_SEND_ERROR_ABORT,<br>
> +     /** Put the display into a fatal error state */<br>
> +     WL_SEND_ERROR_FATAL,<br>
> +     /** Retry the send operation */<br>
> +     WL_SEND_ERROR_RETRY,<br>
> +};<br>
> +<br>
> +/**<br>
> + * \brief A function pointer type for customizing client send error handling<br>
> + *<br>
> + * A client send error strategy handler is a callback function which the client<br>
> + * can set to direct the core client library how to respond if an error is<br>
> + * encountered while sending a message.<br>
> + *<br>
> + * Normally messages are enqueued to an output buffer by the core client<br>
> + * library, and the user of the core client library is expected to call<br>
> + * wl_display_flush() occasionally, but as that is non-blocking, messages can<br>
> + * queue up in the output buffer.  If later calls to send messages happen to<br>
> + * fill up the output buffer, the core client library will make an internal<br>
> + * call to flush the output buffer.  But if the write call unexpectedly fails,<br>
> + * as there is no good way to return an error to the caller, the core client<br>
> + * library will log a message and call abort().<br>
<br>
The analysis of the current situation is correct.<br>
<br>
If you know you are sending lots of requests, you should be calling<br>
wl_display_flush() occasionally. It will return -1 if flushing fails to<br>
let you know you need to wait. The main problem I see with that is when<br>
to attempt to flush.<br></blockquote><div><br></div><div>We actually do have a call to wl_display_flush() in our larger implementation, called frequently as part of ensuring everything is committed to the display. It does currently handle a return of -1, but by waking up the input event processing thread in case the error is fatal, as that thread handles disconnecting and reconnecting. </div><div><br></div><div>On the surface, making it detect an EAGAIN and wait kind of sounds reasonable. But I kind of wonder how often in normal use it would return EAGAIN, and so would force a wait that wouldn't need to, not when there is still some space in the core client output buffer still. My reason for adding the callback and the retry option were specifically to only make the client block if there really was no other way to avoid an error state.</div><div><br></div><div>That said, I actually am willing to detect EAGAIN on wl_display_flush(), and explicitly wait then. In trying out this patch, I only ended up artificially reproducing an EAGAIN error (taking a few seconds to trigger) when I magnified the request traffic by a factor of about 1000x. That is a pretty unreasonable magnification even for a nested server, so while it could happen, I don't think this is actually that important to my project.</div><div><br></div><div>In our case, the Wayland server we connect to is actually a small part of a much larger Chrome UI process on ChromeOS. Despite being a user-space process, it is not likely to be delayed in its processing of client requests, and it seemed much more likely that the connection was dropping for other reasons. Unfortunately I have no data on correlating our anonymous crash reports (call stack only) with any data on why the Chrome processes might have restarted (update? crash?). At least a clean reconnect is much better than a wl_abort() for us, though still not a nice user experience.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
I remember some discussion from the past, where we would make the<br>
soft-buffer in libwayland-client larger and introduce a check API which<br>
would return whether, say, more than half of the space is already<br>
consumed. That would work as a trigger for an extra flush at a point<br>
convenient to the client.</blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
I remember discussing something like that with Daniel Stone, but I<br>
can't find it. Might have been in IRC.<br>
<br>
Or maybe it was more like, double the soft-buffer size, attempt an<br>
automatic flush if it is more than half-full, and if that fails,<br>
continue and raise a flag that the user can query at a convenient time<br>
and do the dance for manual flush and poll for writable. The remaining<br>
soft-buffer should hopefully allow the manual flush before the buffer<br>
runs out.<br></blockquote><div><br></div><div>I believe I saw the exchange at one point in an IRC archive while looking for existing known problems, though I though I remember seeing it for the receive buffers.</div><div><br></div><div>Thinking about it fresh, Maybe that is reasonable as a general option for clients. The one caveat I could think of has to do with MAX_FDS_OUT and it being only 28. My project does use the linux-dmabuf-unstable-v1 protocol, and having more than 14 surfaces in a nested server seems like it could actually happen often. And on second thought, I could see my client filling up all 28 in one frame, thanks to perhaps an ill-timed flood of notifications in addition to normal UI surfaces.</div><div><br></div><div>Hmm, the patch I've uploaded does catch that, but if I remove the RETRY option I'll think I will need to add another special case wl_display_flush() check when creating buffers. In my case I would actually prefer to find out that the fd buffer is completely full before waiting/flushing though.</div><div><div style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial"><br></div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> + *<br>
> + * If instead a send error strategy function is set, the core client library<br>
> + * will call it to allow the client to choose a different strategy.<br>
> + *<br>
> + * The function takes two arguments.  The first is the display context for the<br>
> + * error.  The second is the errno for the failure that occurred.<br>
> + *<br>
> + * The handler is expected to return one of the wl_send_error_strategy values<br>
> + * to indicate how the core client library should proceed.<br>
<br>
This is the detail that bothers me: making the callback to determine<br>
policy. You have no idea in what context that callback will be made.<br></blockquote><div><br></div><div>Sure, but I meant this to be a choice available to the author of the larger client, assuming they had full knowledge of what the consequences of doing anything non-trivial were.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
> + *<br>
> + * If the handler returns WL_CLIENT_ERROR_ABORT, the core client code will<br>
> + * perform the default action -- log an error and then call the C runtime<br>
> + * abort() function.<br>
<br>
This is the old behaviour, which we agree is not nice.<br>
<br>
> + *<br>
> + * If the handler returns WL_CLIENT_ERROR_FATAL, the core client code will<br>
> + * continue, but the display context will be put into a fatal error state, and<br>
> + * the display should no longer be used. Further calls to send messages<br>
> + * will function, but the message will not actually be enqueued.<br>
<br>
This is the behaviour I would prefer for unrecoverable errors. I<br>
actually wrote that down recently:<br>
<a href="https://gitlab.freedesktop.org/wayland/wayland/issues/12" rel="noreferrer" target="_blank">https://gitlab.freedesktop.org/wayland/wayland/issues/12</a><br>
<br></blockquote><div><br></div><div>I hadn't seen that bug before now, and it exactly matches the conditions I observed. Hence my above response that I will go ahead and strip down my change to something simpler.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> + *<br>
> + * If the handler returns WL_CLIENT_ERROR_RETRY, the send operation is retried<br>
> + * immediately on return, and may fail again.  If it does, the handler will be<br>
> + * called again with the new errno. To avoid a busy loop, the handler should<br>
> + * wait on the display connection, or something equivalent.  If after returning<br>
> + * WL_CLIENT_ERROR_RETRY the send operation succeeds, the handler is called one<br>
> + * last time with an errno of zero. This allows the handler to perform any<br>
> + * necessary cleanup now that the send operation has succeeded. For this final<br>
> + * call, the return value is ignored.<br>
> + *<br>
> + * Note that if the handler blocks, the current thread that is making the call<br>
> + * to send a message will of course be blocked. This means the default behavior<br>
> + * of Wayland client calls being non-blocking will no longer be true.<br>
> + *<br>
> + * In a multi threaded environment, this error handler could be called once for<br>
> + * every thread that attempts to send a message on the display and encounters<br>
> + * the error with the connection. It is the responsibility of the handler<br>
> + * function be itself thread safe.<br>
<br>
This third case is the red flag for me.<br>
<br>
How could someone use this efficiently in real life? Do you have<br>
example users for this?<br></blockquote><div><br></div><div>Unfortunately that code is not open source, otherwise I would point you at it.</div><div><br></div><div>My actual use case is a self-contained process that happens to use Wayland for input and output, and that the use of Wayland there is an internal implementation detail. While my client is a nested server, it doesn't expose a Wayland interface to its clients -- I had to support another interface entirely.</div><div><br></div><div>To me there only seems to be one real general pattern for the callback handler when might implement the retry strategy. It is just slightly more complex than the retry_with_poll_handler() from the test I added.</div><div><br></div><div>To sketch it out in quick pseudo-code, it would be:</div></div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><br></div><div><font face="monospace, monospace">int typical_retry_handler(struct wl_display *display, int send_errno) {<br></font></div></div></blockquote><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><font face="monospace, monospace">if (send_errno == 0) { /* retry succeeded */</font></div></div></blockquote></blockquote><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><font face="monospace, monospace">/* clear retry start timestamp */</font></div></div></blockquote></blockquote></blockquote><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><font face="monospace, monospace">}</font></div></div></blockquote></blockquote><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><font face="monospace, monospace"><font face="monospace, monospace" style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial">if (send_errno != EAGAIN) return<span> </span></font><span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">WL_SEND_ERROR_FATAL;</span></font></div><div><font face="monospace, monospace"><span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><font face="monospace, monospace" style="text-decoration-style:initial;text-decoration-color:initial">if ( /* retry timestamp is not yet set */ ) {</font><br></span></font></div></div></blockquote></blockquote><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><font face="monospace, monospace"><span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><font face="monospace, monospace" style="text-decoration-style:initial;text-decoration-color:initial">/* set it */</font></span></font></div></div></blockquote></blockquote></blockquote><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><font face="monospace, monospace"><span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><font face="monospace, monospace" style="text-decoration-style:initial;text-decoration-color:initial">}</font></span></font></div></div></blockquote></blockquote><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><font face="monospace, monospace">if (now - timestamp > limit) </font><span style="font-family:monospace,monospace">return WL_SEND_ERROR_FATAL;</span></div></blockquote></blockquote><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><font face="monospace, monospace"><span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><font face="monospace, monospace" style="text-decoration-style:initial;text-decoration-color:initial">/* poll, using the timestamp to set a timeout */</font></span></font></div></div></blockquote></blockquote><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><font face="monospace, monospace"><span style="background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><font face="monospace, monospace" style="text-decoration-style:initial;text-decoration-color:initial">return WL_SEND_ERROR_RETRY;</font></span></font></div></div></blockquote></blockquote><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div class="gmail_quote"><div><font face="monospace, monospace">}</font></div></div></blockquote><div class="gmail_quote"><div><br></div><div>If your objection is that you would rather this handler be standardized, I can move it to the the only patch, use the existing core client mutex, and having the timestamp be part of the wl_display state. Multiple threads would block for on the send, but they would have done so anyway. Then there could be a public api that selected between the default (abort), error, or error+retry implemented by the above pseudocode.</div><div><br></div><div>But as you said earlier, I will really instead have the wl_client_flush() do the poll().</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
How does the callback know at what kind of point of execution you are<br>
in your application? Can you block there? Can you deal with<br>
disconnection? What if the trigger fires inside a library you use, like<br>
libEGL? Which holds mutexes of its own? What if it happens in a thread<br>
you didn't expect, e.g. from GStreamer?<br>
<br>
You'd probably need to maintain global flag variables across code<br>
blocks. If you maintain something across code blocks, would it not be<br>
easier instead to try a manual flush at the end of a block and see if<br>
that works?<br></blockquote><div><br></div><div>I agree that the retry option would only be useful in fairly restricted cases, and not all clients could actually use it.</div><div><br></div><div>For the client I am dealing with, it is a valid option -- we know where all the send calls are made, and are aware of the consequences of holding the mutexes we do hold, and we are willing to block rather to immediately fail the connection if it might recover.</div><div><br></div><div>But again, certainly cannot justify this part of the change by saying we need it, because I'm fairly certain we do not (though perhaps I do need to worry about fd's). For simplicity then I see no point really in arguing further, and will strip out the retry option from my patch.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
If flushing is inefficient, we could look into adding ABI to get<br>
information about the buffer status, so you can make a better choice on<br>
whether to flush or not.<br>
<br>
If you attempt a manual flush and it fails, you will have more freedom<br>
to handle that situation than with the callback you propose. You can do<br>
everything you could with the callback and you could also choose to<br>
return from the request sending code block and come back later, e.g. on<br>
the next round of your main event loop or when then Wayland fd signals<br>
writable in poll() again. </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Interrupting what you are doing is much easier if you can pick the<br>
points where it is possible. It is hard to be able to handle a<br>
potential callback every time you call a request sending function. </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Especially if the client is a nested server, you probably would not<br>
want to block on the Wayland connection because that could prevent you<br>
from servicing your own clients.<br>
<br></blockquote><div><br></div><div>Interestingly while having that block does prevent the display from being updated, it doesn't (immediately) block our clients, though they might starve for buffers not being released fairly soon after. As a still relatively uncommon error case, blocking briefly (and then disconnecting/reconnecting if needed) is better than taking down everyone due to the wl_abort.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Do you think the alternative solutions could be workable for you?<br></blockquote><div><br></div><div>As above, yes.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
I would be very happy to see someone working on those, this issue has<br>
existed since the beginning.<br>
<br></blockquote><div><br></div><div>I'm happy to help.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Thanks,<br>
pq<br>
<br>
<br>
> + */<br>
> +typedef enum wl_send_error_strategy<br>
> +     (*wl_send_error_handler_func_t)(struct wl_display *, int /* errno */);<br>
> +<br>
> +void<br>
> +wl_display_set_send_error_handler_client(struct wl_display *display,<br>
> +                                      wl_send_error_handler_func_t handler);<br>
> +<br>
>  void<br>
>  wl_log_set_handler_client(wl_log_func_t handler);<br>
>  <br>
> diff --git a/src/wayland-client.c b/src/wayland-client.c<br>
> index efeb745..3a9db6f 100644<br>
> --- a/src/wayland-client.c<br>
> +++ b/src/wayland-client.c<br>
> @@ -113,6 +113,8 @@ struct wl_display {<br>
>       int reader_count;<br>
>       uint32_t read_serial;<br>
>       pthread_cond_t reader_cond;<br>
> +<br>
> +     wl_send_error_handler_func_t send_error_handler;<br>
>  };<br>
>  <br>
>  /** \endcond */<br>
> @@ -696,6 +698,44 @@ wl_proxy_marshal_array_constructor(struct wl_proxy *proxy,<br>
>                                                           proxy->version);<br>
>  }<br>
>  <br>
> +static enum wl_send_error_strategy<br>
> +display_send_error_handler_invoke(struct wl_display *display, int send_errno)<br>
> +{<br>
> +     enum wl_send_error_strategy ret = WL_SEND_ERROR_ABORT;<br>
> +     wl_send_error_handler_func_t handler = display->send_error_handler;<br>
> +     if (handler) {<br>
> +             pthread_mutex_unlock(&display->mutex);<br>
> +             ret = handler(display, send_errno);<br>
> +             pthread_mutex_lock(&display->mutex);<br>
> +     }<br>
> +     return ret;<br>
> +}<br>
> +<br>
> +static void<br>
> +display_closure_send_error(struct wl_display *display,<br>
> +                        struct wl_closure *closure)<br>
> +{<br>
> +     while (true) {<br>
> +             int send_errno = errno;<br>
> +             switch (display_send_error_handler_invoke(display, send_errno)) {<br>
> +             case WL_SEND_ERROR_FATAL:<br>
> +                     display_fatal_error(display, send_errno);<br>
> +                     return;<br>
> +<br>
> +             case WL_SEND_ERROR_RETRY:<br>
> +                     if (!wl_closure_send(closure, display->connection)) {<br>
> +                             display_send_error_handler_invoke(display, 0);<br>
> +                             return;<br>
> +                     }<br>
> +                     break;<br>
> +<br>
> +             case WL_SEND_ERROR_ABORT:<br>
> +             default:<br>
> +                     wl_abort("Error sending request: %s\n",<br>
> +                              strerror(send_errno));<br>
> +             }<br>
> +     }<br>
> +}<br>
>  <br>
>  /** Prepare a request to be sent to the compositor<br>
>   *<br>
> @@ -743,6 +783,9 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,<br>
>                       goto err_unlock;<br>
>       }<br>
>  <br>
> +     if (proxy->display->last_error)<br>
> +             goto err_unlock;<br>
> +<br>
>       closure = wl_closure_marshal(&proxy->object, opcode, args, message);<br>
>       if (closure == NULL)<br>
>               wl_abort("Error marshalling request: %s\n", strerror(errno));<br>
> @@ -751,7 +794,7 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,<br>
>               wl_closure_print(closure, &proxy->object, true);<br>
>  <br>
>       if (wl_closure_send(closure, proxy->display->connection))<br>
> -             wl_abort("Error sending request: %s\n", strerror(errno));<br>
> +             display_closure_send_error(proxy->display, closure);<br>
>  <br>
>       wl_closure_destroy(closure);<br>
>  <br>
> @@ -2000,6 +2043,28 @@ wl_display_flush(struct wl_display *display)<br>
>       return ret;<br>
>  }<br>
>  <br>
> +/** Sets the error handler to invoke when a send fails internally<br>
> +*<br>
> +* \param display The display context object<br>
> +* \param handler The function to call when an error occurs.<br>
> +*<br>
> +* Sets the error handler to call to decide what should happen on a send error.<br>
> +*<br>
> +* If no handler is set, the client will use the WL_SEND_ERROR_ABORT strategy.<br>
> +* This means the core client code will call abort() if if encounters a failure<br>
> +* while sending a message to the server.<br>
> +*<br>
> +* Setting a handler allows a client implementation to alter this behavior.<br>
> +*<br>
> +* \memberof wl_display<br>
> +*/<br>
> +WL_EXPORT void<br>
> +wl_display_set_send_error_handler_client(struct wl_display *display,<br>
> +                                      wl_send_error_handler_func_t handler)<br>
> +{<br>
> +     display->send_error_handler = handler;<br>
> +}<br>
> +<br>
>  /** Set the user data associated with a proxy<br>
>   *<br>
>   * \param proxy The proxy object<br>
</blockquote></div></div>