[libsoup/carlosgc/thread-safe: 14/22] tests: add test to check requests from multiple threads
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/carlosgc/thread-safe: 14/22] tests: add test to check requests from multiple threads
- Date: Sun, 24 Apr 2022 08:19:43 +0000 (UTC)
commit a59e1c11d6a9d60abaa020b8766f9a8edd150645
Author: Carlos Garcia Campos <cgarcia igalia com>
Date: Wed Apr 20 15:03:58 2022 +0200
tests: add test to check requests from multiple threads
tests/meson.build | 1 +
tests/multithread-test.c | 521 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 522 insertions(+)
---
diff --git a/tests/meson.build b/tests/meson.build
index 7851e571..bd76bab9 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -84,6 +84,7 @@ tests = [
{'name': 'logger'},
{'name': 'misc'},
{'name': 'multipart'},
+ {'name': 'multithread'},
{'name': 'no-ssl'},
{'name': 'ntlm'},
{'name': 'redirect'},
diff --git a/tests/multithread-test.c b/tests/multithread-test.c
new file mode 100644
index 00000000..793b19d3
--- /dev/null
+++ b/tests/multithread-test.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright 2022 Igalia S.L.
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#include "test-utils.h"
+
+#include "soup-connection.h"
+#include "soup-message-private.h"
+
+static GUri *base_uri;
+
+typedef enum {
+ BASIC_SYNC = 1 << 0,
+ BASIC_SSL = 1 << 1,
+ BASIC_PROXY = 1 << 2,
+ BASIC_HTTP2 = 1 << 3,
+ BASIC_MAX_CONNS = 1 << 4,
+ BASIC_NO_MAIN_THREAD = 1 << 5
+} BasicTestFlags;
+
+typedef struct {
+ SoupSession *session;
+ BasicTestFlags flags;
+} Test;
+
+#define HTTPS_SERVER "https://127.0.0.1:47525"
+#define HTTP_PROXY "http://127.0.0.1:47526"
+
+static void
+test_setup (Test *test, gconstpointer data)
+{
+ test->flags = GPOINTER_TO_UINT (data);
+ if (test->flags & BASIC_MAX_CONNS)
+ test->session = soup_test_session_new ("max-conns", 1, NULL);
+ else
+ test->session = soup_test_session_new (NULL);
+}
+
+static void
+test_teardown (Test *test, gconstpointer data)
+{
+ soup_test_session_abort_unref (test->session);
+ while (g_main_context_pending (NULL))
+ g_main_context_iteration (NULL, FALSE);
+}
+
+static void
+msg_signal_check_context (SoupMessage *msg,
+ GMainContext *context)
+{
+ g_assert_true (g_object_get_data (G_OBJECT (msg), "thread-context") == context);
+}
+
+static void
+connect_message_signals_to_check_context (SoupMessage *msg,
+ GMainContext *context)
+{
+ g_object_set_data (G_OBJECT (msg), "thread-context", context);
+ g_signal_connect (msg, "starting", G_CALLBACK (msg_signal_check_context), context);
+ g_signal_connect (msg, "wrote-headers", G_CALLBACK (msg_signal_check_context), context);
+ g_signal_connect (msg, "wrote-body", G_CALLBACK (msg_signal_check_context), context);
+ g_signal_connect (msg, "got-headers", G_CALLBACK (msg_signal_check_context), context);
+ g_signal_connect (msg, "got-body", G_CALLBACK (msg_signal_check_context), context);
+ g_signal_connect (msg, "finished", G_CALLBACK (msg_signal_check_context), context);
+}
+
+static void
+msg_signal_check_thread (SoupMessage *msg,
+ GThread *thread)
+{
+ g_assert_true (g_object_get_data (G_OBJECT (msg), "thread-id") == thread);
+}
+
+static void
+connect_message_signals_to_check_thread (SoupMessage *msg,
+ GThread *thread)
+{
+ g_object_set_data (G_OBJECT (msg), "thread-id", thread);
+ g_signal_connect (msg, "starting", G_CALLBACK (msg_signal_check_thread), thread);
+ g_signal_connect (msg, "wrote-headers", G_CALLBACK (msg_signal_check_thread), thread);
+ g_signal_connect (msg, "wrote-body", G_CALLBACK (msg_signal_check_thread), thread);
+ g_signal_connect (msg, "got-headers", G_CALLBACK (msg_signal_check_thread), thread);
+ g_signal_connect (msg, "got-body", G_CALLBACK (msg_signal_check_thread), thread);
+ g_signal_connect (msg, "finished", G_CALLBACK (msg_signal_check_thread), thread);
+}
+
+static void
+message_send_and_read_ready_cb (SoupSession *session,
+ GAsyncResult *result,
+ GMainLoop *loop)
+{
+ GBytes *body;
+ GBytes *index = soup_test_get_index ();
+
+ if (loop)
+ g_assert_true (g_main_loop_get_context (loop) == g_main_context_get_thread_default ());
+
+ body = soup_session_send_and_read_finish (session, result, NULL);
+ g_assert_nonnull (body);
+ g_assert_cmpmem (g_bytes_get_data (body, NULL), g_bytes_get_size (body), g_bytes_get_data (index,
NULL), g_bytes_get_size (index));
+ g_bytes_unref (body);
+
+ if (loop)
+ g_main_loop_quit (loop);
+}
+
+static void
+task_async_function (GTask *task,
+ GObject *source,
+ Test *test,
+ GCancellable *cancellable)
+{
+ GMainContext *context;
+ GMainLoop *loop;
+ SoupMessage *msg;
+
+ context = g_main_context_new ();
+ g_main_context_push_thread_default (context);
+
+ loop = g_main_loop_new (context, FALSE);
+
+ if (test->flags & BASIC_SSL)
+ msg = soup_message_new ("GET", HTTPS_SERVER);
+ else
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ if (test->flags & BASIC_HTTP2)
+ soup_message_set_force_http_version (msg, SOUP_HTTP_2_0);
+ connect_message_signals_to_check_context (msg, context);
+ soup_session_send_and_read_async (test->session, msg, G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback)message_send_and_read_ready_cb,
+ loop);
+ g_object_unref (msg);
+
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ g_task_return_boolean (task, TRUE);
+
+ g_main_context_pop_thread_default (context);
+ g_main_context_unref (context);
+}
+
+static void
+task_sync_function (GTask *task,
+ GObject *source,
+ Test *test,
+ GCancellable *cancellable)
+{
+ SoupMessage *msg;
+ GBytes *body;
+ GBytes *index = soup_test_get_index ();
+
+ if (test->flags & BASIC_SSL)
+ msg = soup_message_new ("GET", HTTPS_SERVER);
+ else
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ if (test->flags & BASIC_HTTP2)
+ soup_message_set_force_http_version (msg, SOUP_HTTP_2_0);
+ connect_message_signals_to_check_thread (msg, g_thread_self ());
+ body = soup_session_send_and_read (test->session, msg, NULL, NULL);
+ g_assert_nonnull (body);
+ g_assert_cmpmem (g_bytes_get_data (body, NULL), g_bytes_get_size (body), g_bytes_get_data (index,
NULL), g_bytes_get_size (index));
+ g_bytes_unref (body);
+ g_object_unref (msg);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+task_finished_cb (GObject *source,
+ GAsyncResult *result,
+ guint *finished_count)
+{
+ g_assert_true (g_task_propagate_boolean (G_TASK (result), NULL));
+ g_atomic_int_inc (finished_count);
+}
+
+static void
+message_finished_cb (SoupMessage *msg,
+ guint *finished_count)
+{
+ g_atomic_int_inc (finished_count);
+}
+
+static void
+do_multithread_basic_test (Test *test,
+ gconstpointer data)
+{
+ SoupMessage *msg = NULL;
+ guint n_msgs = 6;
+ guint n_main_thread_msgs;
+ guint i;
+ guint finished_count = 0;
+
+ if (test->flags & BASIC_PROXY) {
+ GProxyResolver *resolver;
+
+ resolver = g_simple_proxy_resolver_new (HTTP_PROXY, NULL);
+ soup_session_set_proxy_resolver (test->session, resolver);
+ g_object_unref (resolver);
+ }
+
+ n_main_thread_msgs = test->flags & BASIC_NO_MAIN_THREAD ? 0 : 1;
+
+ if (n_main_thread_msgs) {
+ if (test->flags & BASIC_SSL)
+ msg = soup_message_new ("GET", HTTPS_SERVER);
+ else
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ if (test->flags & BASIC_HTTP2)
+ soup_message_set_force_http_version (msg, SOUP_HTTP_2_0);
+ if (test->flags & BASIC_SYNC)
+ connect_message_signals_to_check_thread (msg, g_thread_self ());
+ else
+ connect_message_signals_to_check_context (msg, g_main_context_get_thread_default ());
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (message_finished_cb),
+ &finished_count);
+ soup_session_send_and_read_async (test->session, msg, G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback)message_send_and_read_ready_cb,
+ NULL);
+ }
+
+ for (i = 0; i < n_msgs - n_main_thread_msgs; i++) {
+ GTask *task;
+
+ task = g_task_new (NULL, NULL, (GAsyncReadyCallback)task_finished_cb, &finished_count);
+ g_task_set_task_data (task, test, NULL);
+ g_task_run_in_thread (task, (GTaskThreadFunc)(test->flags & BASIC_SYNC ? task_sync_function
: task_async_function));
+ g_object_unref (task);
+ }
+
+ while (g_atomic_int_get (&finished_count) != n_msgs)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_clear_object (&msg);
+
+ while (g_main_context_pending (NULL))
+ g_main_context_iteration (NULL, FALSE);
+}
+
+static void
+do_multithread_basic_ssl_test (Test *test,
+ gconstpointer data)
+{
+ SOUP_TEST_SKIP_IF_NO_TLS;
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ do_multithread_basic_test (test, data);
+}
+
+static void
+connections_test_msg_starting (SoupMessage *msg,
+ SoupConnection **conn)
+{
+ *conn = g_object_ref (soup_message_get_connection (msg));
+}
+
+static void
+connections_test_task_async_function (GTask *task,
+ GObject *source,
+ Test *test,
+ GCancellable *cancellable)
+{
+ GMainContext *context;
+ SoupMessage *msg;
+ GBytes *body;
+ SoupConnection *conn = NULL;
+
+ context = g_main_context_new ();
+ g_main_context_push_thread_default (context);
+
+ if (test->flags & BASIC_SSL)
+ msg = soup_message_new ("GET", HTTPS_SERVER);
+ else
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ if (test->flags & BASIC_HTTP2)
+ soup_message_set_force_http_version (msg, SOUP_HTTP_2_0);
+ g_signal_connect (msg, "starting",
+ G_CALLBACK (connections_test_msg_starting),
+ &conn);
+ body = soup_test_session_async_send (test->session, msg, NULL, NULL);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+
+ g_task_return_pointer (task, conn, g_object_unref);
+
+ g_main_context_pop_thread_default (context);
+ g_main_context_unref (context);
+}
+
+static void
+connections_test_task_sync_function (GTask *task,
+ GObject *source,
+ Test *test,
+ GCancellable *cancellable)
+{
+ SoupMessage *msg;
+ GBytes *body;
+ SoupConnection *conn = NULL;
+
+ if (test->flags & BASIC_SSL)
+ msg = soup_message_new ("GET", HTTPS_SERVER);
+ else
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ if (test->flags & BASIC_HTTP2)
+ soup_message_set_force_http_version (msg, SOUP_HTTP_2_0);
+ g_signal_connect (msg, "starting",
+ G_CALLBACK (connections_test_msg_starting),
+ &conn);
+ body = soup_session_send_and_read (test->session, msg, NULL, NULL);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+
+ g_task_return_pointer (task, conn, g_object_unref);
+}
+
+static void
+do_multithread_connections_test (Test *test,
+ gconstpointer data)
+{
+ SoupMessage *msg;
+ SoupConnection *conn = NULL;
+ SoupConnection *thread_conn;
+ GBytes *body;
+ GTask *task;
+
+ if (test->flags & BASIC_SSL)
+ msg = soup_message_new ("GET", HTTPS_SERVER);
+ else
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ if (test->flags & BASIC_HTTP2)
+ soup_message_set_force_http_version (msg, SOUP_HTTP_2_0);
+ g_signal_connect (msg, "starting",
+ G_CALLBACK (connections_test_msg_starting),
+ &conn);
+ body = soup_test_session_async_send (test->session, msg, NULL, NULL);
+ g_bytes_unref (body);
+
+ while (g_main_context_pending (NULL))
+ g_main_context_iteration (NULL, FALSE);
+
+ g_assert_nonnull (conn);
+ g_assert_cmpuint (soup_connection_get_state (conn), ==, SOUP_CONNECTION_IDLE);
+
+ /* An idle connection can be reused by another thread */
+ task = g_task_new (NULL, NULL, NULL, NULL);
+ g_task_set_task_data (task, test, NULL);
+ g_task_run_in_thread_sync (task, (GTaskThreadFunc)(test->flags & BASIC_SYNC ?
connections_test_task_sync_function : connections_test_task_async_function));
+ thread_conn = g_task_propagate_pointer (task, NULL);
+ g_object_unref (task);
+ g_assert_true (conn == thread_conn);
+ g_object_unref (thread_conn);
+
+ g_object_unref (conn);
+ g_object_unref (msg);
+}
+
+static void
+do_multithread_connections_http2_test (Test *test,
+ gconstpointer data)
+{
+ SOUP_TEST_SKIP_IF_NO_TLS;
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ do_multithread_connections_test (test, data);
+}
+
+static void
+server_callback (SoupServer *server,
+ SoupServerMessage *msg,
+ const char *path,
+ GHashTable *query,
+ gpointer data)
+{
+ GBytes *index = soup_test_get_index ();
+
+ soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+ soup_server_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ g_bytes_get_data (index, NULL),
+ g_bytes_get_size (index));
+}
+
+int
+main (int argc, char **argv)
+{
+ int ret;
+ SoupServer *server;
+
+ test_init (argc, argv, NULL);
+ apache_init ();
+
+ server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
+ soup_server_add_handler (server, NULL, server_callback, "http", NULL);
+ base_uri = soup_test_server_get_uri (server, "http", NULL);
+
+ g_test_add ("/multithread/basic/async", Test,
+ GUINT_TO_POINTER (0),
+ test_setup,
+ do_multithread_basic_test,
+ test_teardown);
+ g_test_add ("/multithread/basic/sync", Test,
+ GUINT_TO_POINTER (BASIC_SYNC),
+ test_setup,
+ do_multithread_basic_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-ssl/async", Test,
+ GUINT_TO_POINTER (BASIC_SSL),
+ test_setup,
+ do_multithread_basic_ssl_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-ssl/sync", Test,
+ GUINT_TO_POINTER (BASIC_SSL | BASIC_SYNC),
+ test_setup,
+ do_multithread_basic_ssl_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-proxy/async", Test,
+ GUINT_TO_POINTER (BASIC_PROXY),
+ test_setup,
+ do_multithread_basic_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-proxy/sync", Test,
+ GUINT_TO_POINTER (BASIC_PROXY | BASIC_SYNC),
+ test_setup,
+ do_multithread_basic_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-no-main-thread/async", Test,
+ GUINT_TO_POINTER (BASIC_NO_MAIN_THREAD),
+ test_setup,
+ do_multithread_basic_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-no-main-thread/sync", Test,
+ GUINT_TO_POINTER (BASIC_NO_MAIN_THREAD | BASIC_SYNC),
+ test_setup,
+ do_multithread_basic_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-ssl-proxy/async", Test,
+ GUINT_TO_POINTER (BASIC_SSL | BASIC_PROXY),
+ test_setup,
+ do_multithread_basic_ssl_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-ssl-proxy/sync", Test,
+ GUINT_TO_POINTER (BASIC_SSL | BASIC_PROXY | BASIC_SYNC),
+ test_setup,
+ do_multithread_basic_ssl_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-http2/async", Test,
+ GUINT_TO_POINTER (BASIC_HTTP2 | BASIC_SSL),
+ test_setup,
+ do_multithread_basic_ssl_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-http2/sync", Test,
+ GUINT_TO_POINTER (BASIC_HTTP2 | BASIC_SSL | BASIC_SYNC),
+ test_setup,
+ do_multithread_basic_ssl_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-no-main-thread-http2/async", Test,
+ GUINT_TO_POINTER (BASIC_NO_MAIN_THREAD | BASIC_HTTP2 | BASIC_SSL),
+ test_setup,
+ do_multithread_basic_ssl_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-no-main-thread-http2/sync", Test,
+ GUINT_TO_POINTER (BASIC_NO_MAIN_THREAD | BASIC_HTTP2 | BASIC_SSL | BASIC_SYNC),
+ test_setup,
+ do_multithread_basic_ssl_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-max-conns/async", Test,
+ GUINT_TO_POINTER (BASIC_MAX_CONNS),
+ test_setup,
+ do_multithread_basic_test,
+ test_teardown);
+ g_test_add ("/multithread/basic-max-conns/sync", Test,
+ GUINT_TO_POINTER (BASIC_MAX_CONNS | BASIC_SYNC),
+ test_setup,
+ do_multithread_basic_test,
+ test_teardown);
+ g_test_add ("/multithread/connections/async", Test,
+ GUINT_TO_POINTER (0),
+ test_setup,
+ do_multithread_connections_test,
+ test_teardown);
+ g_test_add ("/multithread/connections/sync", Test,
+ GUINT_TO_POINTER (BASIC_SYNC),
+ test_setup,
+ do_multithread_connections_test,
+ test_teardown);
+ g_test_add ("/multithread/connections-http2/async", Test,
+ GUINT_TO_POINTER (BASIC_HTTP2 | BASIC_SSL),
+ test_setup,
+ do_multithread_connections_http2_test,
+ test_teardown);
+ g_test_add ("/multithread/connections-http2/sync", Test,
+ GUINT_TO_POINTER (BASIC_HTTP2 | BASIC_SSL | BASIC_SYNC),
+ test_setup,
+ do_multithread_connections_http2_test,
+ test_teardown);
+
+ ret = g_test_run ();
+
+ g_uri_unref (base_uri);
+ soup_test_server_quit_unref (server);
+ test_cleanup ();
+
+ return ret;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]