[PATCH 3/4] tests: add display test that highlights another bug in read_events
Pekka Paalanen
ppaalanen at gmail.com
Thu Sep 4 06:17:11 PDT 2014
On Fri, 29 Aug 2014 11:21:30 +0200
Marek Chalupa <mchqwerty at gmail.com> wrote:
> When wl_display_read_events() returns with errno == EAGAIN, we
> naturally try to call it again. But this next call results in deadlock.
>
> Signed-off-by: Marek Chalupa <mchqwerty at gmail.com>
> ---
> tests/display-test.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 88 insertions(+)
>
> diff --git a/tests/display-test.c b/tests/display-test.c
> index 1289866..e1cf830 100644
> --- a/tests/display-test.c
> +++ b/tests/display-test.c
> @@ -32,6 +32,7 @@
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <pthread.h>
> +#include <poll.h>
>
> #include "wayland-private.h"
> #include "wayland-server.h"
> @@ -480,6 +481,93 @@ TEST(threading_cancel_read_tst)
> display_destroy(d);
> }
>
> +/* set this to 1 when it is right that
> + * the thread is woken up */
> +static int threads_can_be_woken_up = 0;
> +
> +static void *
> +thread_eagain_func(void *data) {
> + struct client *c = data;
> +
> + register_reading(c->wl_display);
> +
> + c->display_stopped = 1;
> + assert(wl_display_read_events(c->wl_display) == 0
> + && errno != EAGAIN);
> + /* if thread was sleeping in read_events, this assert
> + * must pass now */
> + assert(threads_can_be_woken_up && "Thread woke up too early");
> +
> + pthread_exit(NULL);
> +}
> +
> +static void
> +threading_read_eagain(void)
> +{
> + struct client *c = client_connect();
> + pthread_t th1, th2, th3;
> + struct pollfd fds;
> + int i;
> +
> + register_reading(c->wl_display);
> +
> + th1 = create_thread(c, thread_eagain_func);
> + th2 = create_thread(c, thread_eagain_func);
> + th3 = create_thread(c, thread_eagain_func);
> +
> + /* All the threads are sleeping, waiting until read or cancel
> + * is called. Since we have no data on socket waiting,
> + * the wl_connection_read should end up with error and set errno
> + * to EAGAIN => wl_display_read_events() will return 0 and set errno
> + * to EAGAIN. It should not wake up the sleeping threads, so that
> + * this thread can try to read again */
> + alarm(3);
> + assert(wl_display_read_events(c->wl_display) == 0);
> + assert(errno == EAGAIN);
I think there is a problem here. wl_display_read_events() returning 0
means success, and in that case errno may not be set if it was a real
success. There is no EAGAIN to be handled, it's already done inside
read_events().
In this test it won't be a real success, but you cannot code clients to
check for errno after getting 0 from wl_display_read_events().
> +
> + /* the same should happen next times - but there was a bug when
> + * the main thread got blocked! */
> + for (i = 0; i < 5; ++i) {
> + assert(wl_display_read_events(c->wl_display) == 0);
Therefore you are here calling wl_display_read_events() again after it
succeeded! And that IMO is a developer error.
Besides, I cannot see anywhere where it says that it is ok to call
wl_display_read_events() again even if it failed, without calling
wl_display_prepare_read() first.
But I see something else: wl_display_read_events() does not decrement
reader_count if it returns due to last_error. I think that needs a fix
to guarantee the reader_count is decremented, so that the side-effects
are consistent.
> + assert(errno == EAGAIN);
> + }
> +
> + /* generate something to read and wait until the event comes */
> + wl_proxy_marshal((struct wl_proxy *) c->tc,
> + 0 /* STOP DISPLAY */, 1 /* number */);
> + assert(wl_display_flush(c->wl_display) > 0);
> +
> + fds.fd = wl_display_get_fd(c->wl_display);
> + fds.events = POLLIN;
> + assert(poll(&fds, 1, 3) == 1);
Is timeout of 3 milliseconds intended?
> +
> + /* threads can be woken up now */
> + threads_can_be_woken_up = 1;
> +
> + errno = 0;
> + assert(wl_display_read_events(c->wl_display) == 0);
> + assert(errno != EAGAIN);
> + assert(wl_display_dispatch_pending(c->wl_display) == 1);
> +
> + pthread_join(th1, NULL);
> + pthread_join(th2, NULL);
> + pthread_join(th3, NULL);
> +
> + client_disconnect(c);
> +}
> +
> +TEST(threading_read_eagain_tst)
> +{
> + struct display *d = display_create();
> +
> + client_create(d, threading_read_eagain);
> +
> + display_run(d);
> + display_resume(d);
> +
> + display_destroy(d);
> +}
> +
> static void *
> thread_prepare_and_read2(void *data)
> {
Thanks,
pq
More information about the wayland-devel
mailing list