@samitouri / QOSamiQemu / commits / d5444408c7

tests/qtest/vhost-user-test: Use g_timeout_add() to schedule connect

In vhost-user-test, we currently create a new g_thread to run the connect_thread() function. This function sleeps for 1 second, and then calls test_server_create_chr() to create and configure a chardev: chr = qemu_chr_new(server->chr_name, chr_path, server->context); g_assert(chr); qemu_chr_fe_init(&server->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read, chr_event, NULL, server, server->context, true); This has a race condition, because when we set the 'reconnect-ms=1000' option on the chardev the socket chardev's implementation handles the connect asynchronously, via a background thread and a callback invoked in the main-loop thread. This means that that callback and the test_server_create_chr() call to qemu_chr_fe_set_handlers() can both enter the char-socket code simultaneously. The result is random assertion failures and memory leaks reported by the clang address-sanitizer. Fix this by using g_timeout_source_new() to set up a GSource that will run test_server_connect() on the main-loop thread. This ensures it can't execute in parallel with the callback that the socket chardev sets up. This is similar to how we already handle the reconnect_cb() in test_reconnect(). Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Fabiano Rosas <farosas@suse.de>

Peter Maydell committed Mar 6, 2026 at 17:01 UTC d5444408c7ef4cc17aa02879713a6de828b94967
1 file changed +27 -9
tests/qtest/vhost-user-test.c
+27 -9
@@ -930,26 +930,42 @@ reconnect_cb(gpointer user_data)
930 return FALSE;
931 }
932
933 -static gpointer
934 -connect_thread(gpointer data)
933 +static gboolean connect_cb(gpointer user_data)
934 {
936 - TestServer *s = data;
935 + TestServer *s = user_data;
936
938 - /* wait for qemu to start before first try, to avoid extra warnings */
939 - g_usleep(G_USEC_PER_SEC);
937 test_server_connect(s);
938
942 - return NULL;
939 + /* We only need to be called once */
940 + return G_SOURCE_REMOVE;
941 +}
942 +
943 +/* Initial delay before connect, in milliseconds (1 second) */
944 +#define INITIAL_CONNECT_DELAY_MS (1 * 1000)
945 +
946 +static void test_schedule_connect(TestServer *s)
947 +{
948 + /*
949 + * Wait for a bit for QEMU to start before we first try to connect,
950 + * to avoid extra warnings. We must run the "connect" on the
951 + * main-loop thread so it doesn't race with a callback that
952 + * the socket-chardev sets up on the main-loop.
953 + */
954 + GSource *src = g_timeout_source_new(INITIAL_CONNECT_DELAY_MS);
955 + g_source_set_callback(src, connect_cb, s, NULL);
956 + g_source_attach(src, s->context);
957 + g_source_unref(src);
958 }
959
960 static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg)
961 {
962 TestServer *s = test_server_new("reconnect", arg);
963
949 - g_thread_unref(g_thread_new("connect", connect_thread, s));
964 append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
965 s->vu_ops->append_opts(s, cmd_line, ",server=on");
966
967 + test_schedule_connect(s);
968 +
969 g_test_queue_destroy(vhost_user_test_cleanup, s);
970
971 return s;
@@ -983,10 +999,11 @@ static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg)
999
1000 s->test_fail = true;
1001
986 - g_thread_unref(g_thread_new("connect", connect_thread, s));
1002 append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
1003 s->vu_ops->append_opts(s, cmd_line, ",server=on");
1004
1005 + test_schedule_connect(s);
1006 +
1007 g_test_queue_destroy(vhost_user_test_cleanup, s);
1008
1009 return s;
@@ -998,10 +1015,11 @@ static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg)
1015
1016 s->test_flags = TEST_FLAGS_DISCONNECT;
1017
1001 - g_thread_unref(g_thread_new("connect", connect_thread, s));
1018 append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
1019 s->vu_ops->append_opts(s, cmd_line, ",server=on");
1020
1021 + test_schedule_connect(s);
1022 +
1023 g_test_queue_destroy(vhost_user_test_cleanup, s);
1024
1025 return s;