[Spice-devel] [spice-gtk PATCH v4 6/6] tests: add test to check for zombie GSources

Victor Toso victortoso at redhat.com
Tue Jun 2 09:00:51 PDT 2015


Using g_pollable_input_stream_create_source to generage several dummy
GSources in order to check if giopipe sets all of them to be dispatched.

This test check for zombie GSources during a write_all/read_chunk
operation.
---
 tests/pipe.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 106 insertions(+)

diff --git a/tests/pipe.c b/tests/pipe.c
index a0ddde4..01629da 100644
--- a/tests/pipe.c
+++ b/tests/pipe.c
@@ -21,6 +21,8 @@ typedef struct _Fixture {
     guint16 read_size;
     guint16 total_read;
 
+    GList *sources;
+
     GMainLoop *loop;
     GCancellable *cancellable;
     guint timeout;
@@ -60,6 +62,7 @@ fixture_set_up(Fixture *fixture,
         fixture->buf[i] = 0x42 + i;
     }
 
+    fixture->sources = NULL;
     fixture->cancellable = g_cancellable_new();
     fixture->loop = g_main_loop_new (NULL, FALSE);
     fixture->timeout = g_timeout_add (1000, stop_loop, fixture->loop);
@@ -72,6 +75,9 @@ fixture_tear_down(Fixture *fixture,
     g_clear_object(&fixture->p1);
     g_clear_object(&fixture->p2);
 
+    if (fixture->sources)
+        g_list_free_full(fixture->sources, (GDestroyNotify) g_source_unref);
+
     g_clear_pointer(&fixture->data, g_free);
     g_clear_object(&fixture->cancellable);
     g_source_remove(fixture->timeout);
@@ -365,6 +371,102 @@ test_pipe_concurrent_write(Fixture *f, gconstpointer user_data)
     g_main_loop_run (f->loop);
 }
 
+static void
+write_all_cb_zombie_check(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    Fixture *f = user_data;
+    GError *error = NULL;
+    gsize nbytes;
+    GList *it;
+
+    g_output_stream_write_all_finish(G_OUTPUT_STREAM(source), result, &nbytes, &error);
+    g_assert_no_error(error);
+    g_assert_cmpint(nbytes, ==, f->data_len);
+    g_clear_error(&error);
+
+    for (it = f->sources; it != NULL; it = it->next) {
+        GSource *s = it->data;
+        g_assert_true (g_source_is_destroyed (s));
+    }
+
+    g_main_loop_quit (f->loop);
+}
+
+static gboolean
+source_cb (gpointer user_data)
+{
+    return G_SOURCE_REMOVE;
+}
+
+#define NUM_OF_DUMMY_GSOURCE 1000
+
+static void
+read_chunk_cb_and_do_zombie(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    Fixture *f = user_data;
+    GError *error = NULL;
+    gssize nbytes;
+    gboolean data_match, try_zombie;
+    gint i;
+
+    nbytes = g_input_stream_read_finish(G_INPUT_STREAM(source), result, &error);
+    g_assert_no_error(error);
+    g_assert_cmpint(nbytes, >, 0);
+    data_match = (g_ascii_strncasecmp(f->data + f->total_read, f->buf, nbytes) == 0);
+    g_assert_true(data_match);
+
+    /* Simulate more Pollable GSources created to read from Pipe; This should
+     * not fail but giopipe does not allow concurrent read/write which means
+     * that only the *last* GSource created will be the one that does the actual
+     * read; The other GSources that are still active should be dispatched.
+     * (In this test, only the real GSource created in g_input_stream_read_async
+     * will read the data) */
+
+    /* Create GSources in all iterations besides the last one, simply because
+     * it is convenient! The execution of the last interaction should give enough
+     * time for for all dummy GSources being detached. */
+    f->total_read += nbytes;
+    try_zombie = (f->total_read + f->read_size < f->data_len);
+
+    if (try_zombie) {
+        for (i = 0; i < NUM_OF_DUMMY_GSOURCE/2; i++) {
+            GSource *s = g_pollable_input_stream_create_source(G_POLLABLE_INPUT_STREAM(f->ip2), NULL);
+            g_source_set_callback(s, source_cb, NULL, NULL);
+            g_source_attach(s, NULL);
+            f->sources = g_list_prepend(f->sources, s);
+        }
+    }
+
+    if (f->total_read != f->data_len)
+        g_input_stream_read_async(f->ip2, f->buf, f->read_size, G_PRIORITY_DEFAULT,
+                                  f->cancellable, read_chunk_cb_and_do_zombie, f);
+
+    if (try_zombie) {
+        for (i = 0; i < NUM_OF_DUMMY_GSOURCE/2; i++) {
+            GSource *s = g_pollable_input_stream_create_source(G_POLLABLE_INPUT_STREAM(f->ip2), NULL);
+            g_source_set_callback(s, source_cb, NULL, NULL);
+            g_source_attach(s, NULL);
+            f->sources = g_list_prepend(f->sources, s);
+        }
+    }
+}
+
+static void
+test_pipe_zombie_sources(Fixture *f, gconstpointer user_data)
+{
+    gint i;
+    f->data_len = 64;
+    f->data = get_test_data(f->data_len);
+    f->read_size = 16;
+    f->total_read = 0;
+
+    g_output_stream_write_all_async(f->op1, f->data, f->data_len, G_PRIORITY_DEFAULT,
+                                    f->cancellable, write_all_cb_zombie_check, f);
+    g_input_stream_read_async(f->ip2, f->buf, f->read_size, G_PRIORITY_DEFAULT,
+                              f->cancellable, read_chunk_cb_and_do_zombie, f);
+    g_main_loop_run (f->loop);
+}
+
 int main(int argc, char* argv[])
 {
     setlocale(LC_ALL, "");
@@ -403,6 +505,10 @@ int main(int argc, char* argv[])
                fixture_set_up, test_pipe_concurrent_write,
                fixture_tear_down);
 
+    g_test_add("/pipe/zombie-sources", Fixture, NULL,
+               fixture_set_up, test_pipe_zombie_sources,
+               fixture_tear_down);
+
     g_test_add("/pipe/readclosestream", Fixture, NULL,
                fixture_set_up, test_pipe_readclosestream,
                fixture_tear_down);
-- 
2.4.2



More information about the Spice-devel mailing list