<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On 10 September 2014 11:17, Pekka Paalanen <span dir="ltr"><<a href="mailto:ppaalanen@gmail.com" target="_blank">ppaalanen@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Tue, 9 Sep 2014 13:10:48 +0200<br>
<div><div class="h5">Marek Chalupa <<a href="mailto:mchqwerty@gmail.com">mchqwerty@gmail.com</a>> wrote:<br>
<br>
> On 4 September 2014 15:17, Pekka Paalanen <<a href="mailto:ppaalanen@gmail.com">ppaalanen@gmail.com</a>> wrote:<br>
><br>
> > On Fri, 29 Aug 2014 11:21:30 +0200<br>
> > Marek Chalupa <<a href="mailto:mchqwerty@gmail.com">mchqwerty@gmail.com</a>> wrote:<br>
> ><br>
> > > When wl_display_read_events() returns with errno == EAGAIN, we<br>
> > > naturally try to call it again. But this next call results in<br>
> > > deadlock.<br>
> > ><br>
> > > Signed-off-by: Marek Chalupa <<a href="mailto:mchqwerty@gmail.com">mchqwerty@gmail.com</a>><br>
> > > ---<br>
> > >  tests/display-test.c | 88<br>
> > ++++++++++++++++++++++++++++++++++++++++++++++++++++<br>
> > >  1 file changed, 88 insertions(+)<br>
> > ><br>
> > > diff --git a/tests/display-test.c b/tests/display-test.c<br>
> > > index 1289866..e1cf830 100644<br>
> > > --- a/tests/display-test.c<br>
> > > +++ b/tests/display-test.c<br>
> > > @@ -32,6 +32,7 @@<br>
> > >  #include <sys/types.h><br>
> > >  #include <sys/stat.h><br>
> > >  #include <pthread.h><br>
> > > +#include <poll.h><br>
> > ><br>
> > >  #include "wayland-private.h"<br>
> > >  #include "wayland-server.h"<br>
> > > @@ -480,6 +481,93 @@ TEST(threading_cancel_read_tst)<br>
> > >       display_destroy(d);<br>
> > >  }<br>
> > ><br>
> > > +/* set this to 1 when it is right that<br>
> > > + * the thread is woken up */<br>
> > > +static int threads_can_be_woken_up = 0;<br>
> > > +<br>
> > > +static void *<br>
> > > +thread_eagain_func(void *data) {<br>
> > > +     struct client *c = data;<br>
> > > +<br>
> > > +     register_reading(c->wl_display);<br>
> > > +<br>
> > > +     c->display_stopped = 1;<br>
> > > +     assert(wl_display_read_events(c->wl_display) == 0<br>
> > > +             && errno != EAGAIN);<br>
> > > +     /* if thread was sleeping in read_events, this assert<br>
> > > +      * must pass now */<br>
> > > +     assert(threads_can_be_woken_up && "Thread woke up too<br>
> > > early"); +<br>
> > > +     pthread_exit(NULL);<br>
> > > +}<br>
> > > +<br>
> > > +static void<br>
> > > +threading_read_eagain(void)<br>
> > > +{<br>
> > > +     struct client *c = client_connect();<br>
> > > +     pthread_t th1, th2, th3;<br>
> > > +     struct pollfd fds;<br>
> > > +     int i;<br>
> > > +<br>
> > > +     register_reading(c->wl_display);<br>
> > > +<br>
> > > +     th1 = create_thread(c, thread_eagain_func);<br>
> > > +     th2 = create_thread(c, thread_eagain_func);<br>
> > > +     th3 = create_thread(c, thread_eagain_func);<br>
> > > +<br>
> > > +     /* All the threads are sleeping, waiting until read or<br>
> > > cancel<br>
> > > +      * is called. Since we have no data on socket waiting,<br>
> > > +      * the wl_connection_read should end up with error and set<br>
> > > errno<br>
> > > +      * to EAGAIN => wl_display_read_events() will return 0 and<br>
> > > set<br>
> > errno<br>
> > > +      * to EAGAIN. It should not wake up the sleeping threads,<br>
> > > so that<br>
> > > +      * this thread can try to read again */<br>
> > > +     alarm(3);<br>
> > > +     assert(wl_display_read_events(c->wl_display) == 0);<br>
> > > +     assert(errno == EAGAIN);<br>
> ><br>
> > I think there is a problem here. wl_display_read_events() returning<br>
> > 0 means success, and in that case errno may not be set if it was a<br>
> > real success. There is no EAGAIN to be handled, it's already done<br>
> > inside read_events().<br>
> ><br>
> > In this test it won't be a real success, but you cannot code<br>
> > clients to check for errno after getting 0 from<br>
> > wl_display_read_events().<br>
> ><br>
><br>
> > > +<br>
> > > +     /* the same should happen next times - but there was a bug<br>
> > > when<br>
> > > +      * the main thread got blocked! */<br>
> > > +     for (i = 0; i < 5; ++i) {<br>
> > > +             assert(wl_display_read_events(c->wl_display) == 0);<br>
> ><br>
> > Therefore you are here calling wl_display_read_events() again after<br>
> > it succeeded! And that IMO is a developer error.<br>
> ><br>
><br>
> yes<br>
><br>
><br>
> > Besides, I cannot see anywhere where it says that it is ok to call<br>
> > wl_display_read_events() again even if it failed, without calling<br>
> > wl_display_prepare_read() first.<br>
> ><br>
> ><br>
> Since original piece of code returned 0 on EAGAIN, then I suppose the<br>
> author considered this as a success, so yes, wl_display_read_events()<br>
> is not probably meant<br>
> to be called again. But consider this:<br>
> if the thread that got EAGAIN (the only thread that is not sleeping)<br>
> calls wl_display_prepare_read()<br>
> + wl_display_read_events() again, it works. Problem is that it does<br>
> not have to call it<br>
> right after the failed wl_display_read_events and the other threads<br>
> are still sleeping.<br>
<br>
</div></div>EAGAIN is not a failure here. It is only a spurious wakeup, and all<br>
threads should go back sleeping on whatever they need to sleep on,<br>
usually poll().<br>
<span class=""><br>
> Moreover, if there are not events on the socket for some time, this<br>
> one thread is looping in cycle<br>
> while the other threads are sleeping.<br>
<br>
</span>It's not a busy-loop, because the thread would be sleeping in poll() or<br>
equivalent.<br>
<br>
But yes, I see that the other threads sleeping inside their<br>
read_events() are not woken up, so they do not do a cycle. I'm not sure<br>
if that is a problem or not. I suppose waking everyone up would not<br>
hurt, but not waking up would risk deadlocking if the thread with<br>
EAGAIN never read again.<br>
<span class=""><br>
> What I meant to do was to give the programmer the choice whether to<br>
> loop or let<br>
> all threads try it again (cancel read). But as you said before, 0 is<br>
> wrong return value for that.<br>
> See end of the e-mail for conclusions.<br>
<br>
</span>Yeah, it seems to be wrong to require cancel_read() after calling<br>
read_events(), no matter whether it succeeded or not.<br>
<span class=""><br>
> Summing it up, in my point of view we have these options:<br>
><br>
> * Let it as it is:<br>
>      this can lead to starving of other threads for some time.<br>
> Moreover, if the thread that<br>
>      got EAGAIN decides to exit (or simply not to read again) instead<br>
> of another trial to read, the other threads will block.<br>
<br>
</span>Yup, I agree with the analysis here.<br>
<span class=""><br>
> * return -1 and set EAGAIN<br>
>     programmer can either try to read again, or cancel read and let<br>
> all threads do another cycle. Good thing is<br>
>     that programmer knows that there was an error and can cancel read<br>
> in the case that the thread is exiting<br>
>     instead of another cycle.<br>
<br>
</span>EAGAIN is not an error, which would make this solution an ABI break.<br>
<span class=""><br>
>  * wake up threads on EAGAIN<br>
>     this solution will force all threads to do another cycle (or<br>
> whatever they want) and should not have any side effects. It can lead<br>
> to big overhead if there are no events on the socket for some time<br>
> (waking up threads again and again in cycle)<br>
<br>
</span>I think we should do this. I don't really see the overhead at all,<br>
since every thread should have poll() in its loop, so they will not be<br>
busy-looping.<br></blockquote><div><br></div><div>If there is a poll, then the overhead is solved :)<br></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>
After all, the sleep inside read_events() is only for synchronizing<br>
reading threads, not for waiting new data to appear.<br>
<br>
EAGAIN in read_events() is a success, even though there was nothing<br>
received. On success (and on failure!) all threads sleeping inside<br>
read_events() should be woken up. IOW, whatever thread hits the<br>
reader_count==0 case in read_events() should *always* end up calling<br>
display_wakeup_threads() before returning.<br>
<br>
Does this make sense?<br></blockquote><div><br></div><div>It does and I agree this is the most safe solution.<br>I'll rebase the old <a href="http://lists.freedesktop.org/archives/wayland-devel/2014-August/016932.html">http://lists.freedesktop.org/archives/wayland-devel/2014-August/016932.html</a><br></div><div>that was created exactly because of this reasoning and send it here.<br></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>
In fact, I think wl_display_read_event() failing on last_error should<br>
be equivalent to calling wl_display_cancel_read(). It would be the same<br>
if the app manually first checked for error and then canceled.<br>
<br></blockquote><div><br></div><div>Sounds reasonable, I'll add a patch for it to the previous one.<br></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>
</blockquote></div><br></div><div class="gmail_extra">Thanks,<br>Marek<br></div></div>