[libsoup] Add SoupSession:use-thread-context



commit 05dff333a7f46eb6644d28a4e15398d347b9a523
Author: Dan Winship <danw gnome org>
Date:   Mon Oct 3 14:58:06 2011 -0400

    Add SoupSession:use-thread-context
    
    Add a SoupSession flag telling it to use
    g_main_context_get_thread_default() on a per-message basis, rather
    than using a single GMainContext for everything. In the simple case,
    this is just more glib-like API. In the more complicated case, it
    allows synchronously sending one or more messages on a
    SoupSessionAsync without running the main GMainLoop.
    
    https://bugs.webkit.org/show_bug.cgi?id=68238

 libsoup/soup-connection.c    |   26 ++++++-
 libsoup/soup-connection.h    |    1 +
 libsoup/soup-message-queue.c |    1 +
 libsoup/soup-message-queue.h |    1 +
 libsoup/soup-session-async.c |   34 ++++++--
 libsoup/soup-session.c       |   56 ++++++++++++-
 libsoup/soup-session.h       |    1 +
 libsoup/soup-socket.c        |   49 ++++++++++-
 libsoup/soup-socket.h        |    1 +
 tests/context-test.c         |  180 ++++++++++++++++++++++++++++++++++++------
 10 files changed, 308 insertions(+), 42 deletions(-)
---
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index d82832f..3e7f0ee 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -37,7 +37,8 @@ typedef struct {
 	GTlsDatabase *tlsdb;
 	gboolean     ssl, ssl_strict, ssl_fallback;
 
-	GMainContext      *async_context;
+	GMainContext *async_context;
+	gboolean      use_thread_context;
 
 	SoupMessageQueueItem *cur_item;
 	SoupConnectionState state;
@@ -67,6 +68,7 @@ enum {
 	PROP_SSL_STRICT,
 	PROP_SSL_FALLBACK,
 	PROP_ASYNC_CONTEXT,
+	PROP_USE_THREAD_CONTEXT,
 	PROP_TIMEOUT,
 	PROP_IDLE_TIMEOUT,
 	PROP_STATE,
@@ -214,6 +216,13 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
 				      "GMainContext to dispatch this connection's async I/O in",
 				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 	g_object_class_install_property (
+		object_class, PROP_USE_THREAD_CONTEXT,
+		g_param_spec_boolean (SOUP_CONNECTION_USE_THREAD_CONTEXT,
+				      "Use thread context",
+				      "Use g_main_context_get_thread_default",
+				      FALSE,
+				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (
 		object_class, PROP_TIMEOUT,
 		g_param_spec_uint (SOUP_CONNECTION_TIMEOUT,
 				   "Timeout value",
@@ -295,6 +304,9 @@ set_property (GObject *object, guint prop_id,
 		if (priv->async_context)
 			g_main_context_ref (priv->async_context);
 		break;
+	case PROP_USE_THREAD_CONTEXT:
+		priv->use_thread_context = g_value_get_boolean (value);
+		break;
 	case PROP_TIMEOUT:
 		priv->io_timeout = g_value_get_uint (value);
 		break;
@@ -341,6 +353,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_ASYNC_CONTEXT:
 		g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
 		break;
+	case PROP_USE_THREAD_CONTEXT:
+		g_value_set_boolean (value, priv->use_thread_context);
+		break;
 	case PROP_TIMEOUT:
 		g_value_set_uint (value, priv->io_timeout);
 		break;
@@ -533,6 +548,7 @@ soup_connection_connect_async (SoupConnection *conn,
 				 SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
 				 SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback,
 				 SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
+				 SOUP_SOCKET_USE_THREAD_CONTEXT, priv->use_thread_context,
 				 SOUP_SOCKET_TIMEOUT, priv->io_timeout,
 				 "clean-dispose", TRUE,
 				 NULL);
@@ -672,6 +688,7 @@ soup_connection_start_ssl_async (SoupConnection   *conn,
 	SoupConnectionPrivate *priv;
 	const char *server_name;
 	SoupConnectionAsyncConnectData *data;
+	GMainContext *async_context;
 
 	g_return_if_fail (SOUP_IS_CONNECTION (conn));
 	priv = SOUP_CONNECTION_GET_PRIVATE (conn);
@@ -681,12 +698,17 @@ soup_connection_start_ssl_async (SoupConnection   *conn,
 	data->callback = callback;
 	data->callback_data = user_data;
 
+	if (priv->use_thread_context)
+		async_context = g_main_context_get_thread_default ();
+	else
+		async_context = priv->async_context;
+
 	server_name = soup_address_get_name (priv->tunnel_addr ?
 					     priv->tunnel_addr :
 					     priv->remote_addr);
 	if (!soup_socket_start_proxy_ssl (priv->socket, server_name,
 					  cancellable)) {
-		soup_add_completion (priv->async_context,
+		soup_add_completion (async_context,
 				     idle_start_ssl_completed, data);
 		return;
 	}
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index cd663a8..72e6106 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -47,6 +47,7 @@ typedef void  (*SoupConnectionCallback)        (SoupConnection   *conn,
 #define SOUP_CONNECTION_SSL_STRICT      "ssl-strict"
 #define SOUP_CONNECTION_SSL_FALLBACK    "ssl-fallback"
 #define SOUP_CONNECTION_ASYNC_CONTEXT   "async-context"
+#define SOUP_CONNECTION_USE_THREAD_CONTEXT "use-thread-context"
 #define SOUP_CONNECTION_TIMEOUT         "timeout"
 #define SOUP_CONNECTION_IDLE_TIMEOUT    "idle-timeout"
 #define SOUP_CONNECTION_STATE           "state"
diff --git a/libsoup/soup-message-queue.c b/libsoup/soup-message-queue.c
index 59c5c5c..d324597 100644
--- a/libsoup/soup-message-queue.c
+++ b/libsoup/soup-message-queue.c
@@ -103,6 +103,7 @@ soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
 
 	item = g_slice_new0 (SoupMessageQueueItem);
 	item->session = queue->session;
+	item->async_context = soup_session_get_async_context (item->session);
 	item->queue = queue;
 	item->msg = g_object_ref (msg);
 	item->callback = callback;
diff --git a/libsoup/soup-message-queue.h b/libsoup/soup-message-queue.h
index 43cb0ae..5fb14c4 100644
--- a/libsoup/soup-message-queue.h
+++ b/libsoup/soup-message-queue.h
@@ -39,6 +39,7 @@ struct _SoupMessageQueueItem {
 	SoupMessage *msg;
 	SoupSessionCallback callback;
 	gpointer callback_data;
+	GMainContext *async_context;
 
 	GCancellable *cancellable;
 	SoupAddress *proxy_addr;
diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c
index c813931..edb6239 100644
--- a/libsoup/soup-session-async.c
+++ b/libsoup/soup-session-async.c
@@ -47,13 +47,25 @@ static void  auth_required   (SoupSession *session, SoupMessage *msg,
 G_DEFINE_TYPE (SoupSessionAsync, soup_session_async, SOUP_TYPE_SESSION)
 
 typedef struct {
-	GSource *idle_run_queue_source;
+	GHashTable *idle_run_queue_sources;
+
 } SoupSessionAsyncPrivate;
 #define SOUP_SESSION_ASYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsyncPrivate))
 
 static void
+destroy_unref_source (gpointer source)
+{
+	g_source_destroy (source);
+	g_source_unref (source);
+}
+
+static void
 soup_session_async_init (SoupSessionAsync *sa)
 {
+	SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (sa);
+
+	priv->idle_run_queue_sources =
+		g_hash_table_new_full (NULL, NULL, NULL, destroy_unref_source);
 }
 
 static void
@@ -61,8 +73,7 @@ finalize (GObject *object)
 {
 	SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (object);
 
-	if (priv->idle_run_queue_source)
-		g_source_destroy (priv->idle_run_queue_source);
+	g_hash_table_destroy (priv->idle_run_queue_sources);
 
 	G_OBJECT_CLASS (soup_session_async_parent_class)->finalize (object);
 }
@@ -364,6 +375,9 @@ process_queue_item (SoupMessageQueueItem *item,
 	SoupSession *session = item->session;
 	SoupProxyURIResolver *proxy_resolver;
 
+	if (item->async_context != soup_session_get_async_context (session))
+		return;
+
 	do {
 		if (item->paused)
 			return;
@@ -470,7 +484,8 @@ idle_run_queue (gpointer sa)
 {
 	SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (sa);
 
-	priv->idle_run_queue_source = NULL;
+	g_hash_table_remove (priv->idle_run_queue_sources,
+			     soup_session_get_async_context (sa));
 	run_queue (sa);
 	return FALSE;
 }
@@ -480,10 +495,13 @@ do_idle_run_queue (SoupSession *session)
 {
 	SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (session);
 
-	if (!priv->idle_run_queue_source) {
-		priv->idle_run_queue_source = soup_add_completion (
-			soup_session_get_async_context (session),
-			idle_run_queue, session);
+	if (!g_hash_table_lookup (priv->idle_run_queue_sources,
+				  soup_session_get_async_context (session))) {
+		GMainContext *async_context = soup_session_get_async_context (session);
+		GSource *source = soup_add_completion (async_context, idle_run_queue, session);
+
+		g_hash_table_insert (priv->idle_run_queue_sources,
+				     async_context, g_source_ref (source));
 	}
 }
 
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 7f5ea0e..16b150a 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -101,6 +101,7 @@ typedef struct {
 	GMutex *host_lock;
 
 	GMainContext *async_context;
+	gboolean use_thread_context;
 
 	GResolver *resolver;
 
@@ -156,6 +157,7 @@ enum {
 	PROP_TLS_DATABASE,
 	PROP_SSL_STRICT,
 	PROP_ASYNC_CONTEXT,
+	PROP_USE_THREAD_CONTEXT,
 	PROP_TIMEOUT,
 	PROP_USER_AGENT,
 	PROP_ACCEPT_LANGUAGE,
@@ -653,7 +655,7 @@ soup_session_class_init (SoupSessionClass *session_class)
 	 *
 	 * Alias for the #SoupSession:async-context property. (The
 	 * session's #GMainContext.)
-	 **/
+	 */
 	g_object_class_install_property (
 		object_class, PROP_ASYNC_CONTEXT,
 		g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
@@ -661,6 +663,33 @@ soup_session_class_init (SoupSessionClass *session_class)
 				      "The GMainContext to dispatch async I/O in",
 				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 	/**
+	 * SOUP_SESSION_USE_THREAD_CONTEXT:
+	 *
+	 * Alias for the #SoupSession:use-thread-context property, qv.
+	 *
+	 * Since: 2.38
+	 */
+	/**
+	 * SoupSession:use-thread-context:
+	 *
+	 * If set, asynchronous operations in this session will run in
+	 * whatever the thread-default #GMainContext is at the time
+	 * they are started, rather than always occurring in a context
+	 * fixed at the session's construction time. "Bookkeeping"
+	 * tasks (like expiring idle connections) will happen in the
+	 * context that was thread-default at the time the session was
+	 * created.
+	 *
+	 * Since: 2.38
+	 */
+	g_object_class_install_property (
+		object_class, PROP_USE_THREAD_CONTEXT,
+		g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
+				      "Use thread-default GMainContext",
+				      "Whether to use thread-default main contexts",
+				      FALSE,
+				      G_PARAM_READWRITE));
+	/**
 	 * SOUP_SESSION_TIMEOUT:
 	 *
 	 * Alias for the #SoupSession:timeout property. (The timeout
@@ -1109,6 +1138,16 @@ set_property (GObject *object, guint prop_id,
 		if (priv->async_context)
 			g_main_context_ref (priv->async_context);
 		break;
+	case PROP_USE_THREAD_CONTEXT:
+		priv->use_thread_context = g_value_get_boolean (value);
+		if (priv->use_thread_context) {
+			if (priv->async_context)
+				g_main_context_unref (priv->async_context);
+			priv->async_context = g_main_context_get_thread_default ();
+			if (priv->async_context)
+				g_main_context_ref (priv->async_context);
+		}
+		break;
 	case PROP_TIMEOUT:
 		priv->io_timeout = g_value_get_uint (value);
 		break;
@@ -1216,6 +1255,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_ASYNC_CONTEXT:
 		g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
 		break;
+	case PROP_USE_THREAD_CONTEXT:
+		g_value_set_boolean (value, priv->use_thread_context);
+		break;
 	case PROP_TIMEOUT:
 		g_value_set_uint (value, priv->io_timeout);
 		break;
@@ -1294,6 +1336,9 @@ uri_is_https (SoupSessionPrivate *priv, SoupURI *uri)
  * context, so you will need to ref it yourself if you want it to
  * outlive its session.
  *
+ * If #SoupSession:use-thread-context is true, this will return the
+ * current thread-default main context.
+ *
  * Return value: (transfer none): @session's #GMainContext, which may
  * be %NULL
  **/
@@ -1305,7 +1350,10 @@ soup_session_get_async_context (SoupSession *session)
 	g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
 	priv = SOUP_SESSION_GET_PRIVATE (session);
 
-	return priv->async_context;
+	if (priv->use_thread_context)
+		return g_main_context_get_thread_default ();
+	else
+		return priv->async_context;
 }
 
 /* Hosts */
@@ -1784,6 +1832,7 @@ soup_session_get_connection (SoupSession *session,
 		SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb,
 		SOUP_CONNECTION_SSL_STRICT, (priv->tlsdb != NULL) && priv->ssl_strict,
 		SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
+		SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
 		SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
 		SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
 		SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
@@ -2234,7 +2283,8 @@ soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
 	addr = g_object_ref (host->addr);
 	g_mutex_unlock (priv->host_lock);
 
-	soup_address_resolve_async (addr, priv->async_context,
+	soup_address_resolve_async (addr,
+				    soup_session_get_async_context (session),
 				    NULL, NULL, NULL);
 	g_object_unref (addr);
 }
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index 349cfdb..dd09599 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -68,6 +68,7 @@ GType soup_session_get_type (void);
 #define SOUP_SESSION_TLS_DATABASE           "tls-database"
 #define SOUP_SESSION_SSL_STRICT             "ssl-strict"
 #define SOUP_SESSION_ASYNC_CONTEXT          "async-context"
+#define SOUP_SESSION_USE_THREAD_CONTEXT     "use-thread-context"
 #define SOUP_SESSION_TIMEOUT                "timeout"
 #define SOUP_SESSION_USER_AGENT             "user-agent"
 #define SOUP_SESSION_ACCEPT_LANGUAGE        "accept-language"
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index ee83a3f..e1f7434 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -55,6 +55,7 @@ enum {
 	PROP_SSL_STRICT,
 	PROP_SSL_FALLBACK,
 	PROP_ASYNC_CONTEXT,
+	PROP_USE_THREAD_CONTEXT,
 	PROP_TIMEOUT,
 	PROP_TRUSTED_CERTIFICATE,
 	PROP_CLEAN_DISPOSE,
@@ -78,6 +79,7 @@ typedef struct {
 	guint ssl_strict:1;
 	guint ssl_fallback:1;
 	guint clean_dispose:1;
+	guint use_thread_context:1;
 	gpointer ssl_creds;
 
 	GMainContext   *async_context;
@@ -406,6 +408,29 @@ soup_socket_class_init (SoupSocketClass *socket_class)
 				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
 	/**
+	 * SOUP_SOCKET_USE_THREAD_CONTEXT:
+	 *
+	 * Alias for the #SoupSocket:use-thread-context property. (Use
+	 * g_main_context_get_thread_default())
+	 *
+	 * Since: 2.36.1
+	 */
+	/**
+	 * SoupSocket:use-thread-context:
+	 *
+	 * Use g_main_context_get_thread_default().
+	 *
+	 * Since: 2.36.1
+	 */
+	g_object_class_install_property (
+		object_class, PROP_USE_THREAD_CONTEXT,
+		g_param_spec_boolean (SOUP_SOCKET_USE_THREAD_CONTEXT,
+				      "Use thread context",
+				      "Use g_main_context_get_thread_default",
+				      FALSE,
+				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
 	 * SOUP_SOCKET_TIMEOUT:
 	 *
 	 * Alias for the #SoupSocket:timeout property. (The timeout
@@ -509,6 +534,9 @@ set_property (GObject *object, guint prop_id,
 		if (priv->async_context)
 			g_main_context_ref (priv->async_context);
 		break;
+	case PROP_USE_THREAD_CONTEXT:
+		priv->use_thread_context = g_value_get_boolean (value);
+		break;
 	case PROP_TIMEOUT:
 		priv->timeout = g_value_get_uint (value);
 		if (priv->conn)
@@ -557,6 +585,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_ASYNC_CONTEXT:
 		g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
 		break;
+	case PROP_USE_THREAD_CONTEXT:
+		g_value_set_boolean (value, priv->use_thread_context);
+		break;
 	case PROP_TIMEOUT:
 		g_value_set_uint (value, priv->timeout);
 		break;
@@ -648,7 +679,7 @@ async_connected (GObject *client, GAsyncResult *result, gpointer data)
 	GSocketConnection *conn;
 	guint status;
 
-	if (priv->async_context)
+	if (priv->async_context && !priv->use_thread_context)
 		g_main_context_pop_thread_default (priv->async_context);
 
 	conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client),
@@ -694,7 +725,7 @@ soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
 
 	priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
 
-	if (priv->async_context)
+	if (priv->async_context && !priv->use_thread_context)
 		g_main_context_push_thread_default (priv->async_context);
 
 	client = g_socket_client_new ();
@@ -765,13 +796,20 @@ soup_socket_create_watch (SoupSocketPrivate *priv, GIOCondition cond,
 			  GCancellable *cancellable)
 {
 	GSource *watch;
+	GMainContext *async_context;
 
 	if (cond == G_IO_IN)
 		watch = g_pollable_input_stream_create_source (priv->istream, cancellable);
 	else
 		watch = g_pollable_output_stream_create_source (priv->ostream, cancellable);
 	g_source_set_callback (watch, (GSourceFunc)callback, user_data, NULL);
-	g_source_attach (watch, priv->async_context);
+
+	if (priv->use_thread_context)
+		async_context = g_main_context_get_thread_default ();
+	else
+		async_context = priv->async_context;
+
+	g_source_attach (watch, async_context);
 	g_source_unref (watch);
 
 	return watch;
@@ -793,6 +831,7 @@ listen_watch (GObject *pollable, gpointer data)
 	new_priv->gsock = new_gsock;
 	if (priv->async_context)
 		new_priv->async_context = g_main_context_ref (priv->async_context);
+	new_priv->use_thread_context = priv->use_thread_context;
 	new_priv->non_blocking = priv->non_blocking;
 	new_priv->is_server = TRUE;
 	new_priv->ssl = priv->ssl;
@@ -1018,7 +1057,7 @@ handshake_async_ready (GObject *source, GAsyncResult *result, gpointer user_data
 	GError *error = NULL;
 	guint status;
 
-	if (priv->async_context)
+	if (priv->async_context && !priv->use_thread_context)
 		g_main_context_pop_thread_default (priv->async_context);
 
 	if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (priv->conn),
@@ -1052,7 +1091,7 @@ soup_socket_handshake_async (SoupSocket         *sock,
 	data->callback = callback;
 	data->user_data = user_data;
 
-	if (priv->async_context)
+	if (priv->async_context && !priv->use_thread_context)
 		g_main_context_push_thread_default (priv->async_context);
 	g_tls_connection_handshake_async (G_TLS_CONNECTION (priv->conn),
 					  G_PRIORITY_DEFAULT,
diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h
index 4d1550f..dc6b59c 100644
--- a/libsoup/soup-socket.h
+++ b/libsoup/soup-socket.h
@@ -48,6 +48,7 @@ typedef struct {
 #define SOUP_SOCKET_SSL_FALLBACK        "ssl-fallback"
 #define SOUP_SOCKET_TRUSTED_CERTIFICATE "trusted-certificate"
 #define SOUP_SOCKET_ASYNC_CONTEXT       "async-context"
+#define SOUP_SOCKET_USE_THREAD_CONTEXT  "use-thread-context"
 #define SOUP_SOCKET_TIMEOUT             "timeout"
 #define SOUP_SOCKET_TLS_CERTIFICATE     "tls-certificate"
 #define SOUP_SOCKET_TLS_ERRORS          "tls-errors"
diff --git a/tests/context-test.c b/tests/context-test.c
index fe9af31..b72d200 100644
--- a/tests/context-test.c
+++ b/tests/context-test.c
@@ -99,34 +99,37 @@ static gpointer test1_thread (gpointer user_data);
 
 static GCond *test1_cond;
 static GMutex *test1_mutex;
+static GMainLoop *test1_loop;
 
 static void
-do_test1 (void)
+do_test1 (int n, gboolean use_thread_context)
 {
-	GMainLoop *loop;
-
-	debug_printf (1, "Test 1: blocking the main thread does not block other thread\n");
+	debug_printf (1, "\nTest %d: blocking the main thread does not block other thread\n", n);
+	if (use_thread_context)
+		debug_printf (1, "(Using g_main_context_push_thread_default())\n");
+	else
+		debug_printf (1, "(Using SOUP_SESSION_ASYNC_CONTEXT)\n");
 
 	test1_cond = g_cond_new ();
 	test1_mutex = g_mutex_new ();
 
-	loop = g_main_loop_new (NULL, FALSE);
-	g_idle_add (idle_start_test1_thread, loop);
-	g_main_loop_run (loop);
-	g_main_loop_unref (loop);
+	test1_loop = g_main_loop_new (NULL, FALSE);
+	g_idle_add (idle_start_test1_thread, GINT_TO_POINTER (use_thread_context));
+	g_main_loop_run (test1_loop);
+	g_main_loop_unref (test1_loop);
 
 	g_mutex_free (test1_mutex);
 	g_cond_free (test1_cond);
 }
 
 static gboolean
-idle_start_test1_thread (gpointer loop)
+idle_start_test1_thread (gpointer use_thread_context)
 {
 	GTimeVal time;
 	GThread *thread;
 
 	g_mutex_lock (test1_mutex);
-	thread = g_thread_create (test1_thread, base_uri, TRUE, NULL);
+	thread = g_thread_create (test1_thread, use_thread_context, TRUE, NULL);
 
 	g_get_current_time (&time);
 	time.tv_sec += 5;
@@ -138,7 +141,7 @@ idle_start_test1_thread (gpointer loop)
 	}
 
 	g_mutex_unlock (test1_mutex);
-	g_main_loop_quit (loop);
+	g_main_loop_quit (test1_loop);
 	return FALSE;
 }
 
@@ -149,7 +152,7 @@ test1_finished (SoupSession *session, SoupMessage *msg, gpointer loop)
 }
 
 static gpointer
-test1_thread (gpointer user_data)
+test1_thread (gpointer use_thread_context)
 {
 	SoupSession *session;
 	GMainContext *async_context;
@@ -162,10 +165,16 @@ test1_thread (gpointer user_data)
 	g_mutex_unlock (test1_mutex);
 
 	async_context = g_main_context_new ();
-	session = soup_test_session_new (
-		SOUP_TYPE_SESSION_ASYNC,
-		SOUP_SESSION_ASYNC_CONTEXT, async_context,
-		NULL);
+	if (use_thread_context) {
+		g_main_context_push_thread_default (async_context);
+		session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+						 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+						 NULL);
+	} else {
+		session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+						 SOUP_SESSION_ASYNC_CONTEXT, async_context,
+						 NULL);
+	}
 	g_main_context_unref (async_context);
 
 	uri = g_build_filename (base_uri, "slow", NULL);
@@ -198,6 +207,9 @@ test1_thread (gpointer user_data)
 	g_free (uri);
 
 	g_cond_signal (test1_cond);
+
+	if (use_thread_context)
+		g_main_context_pop_thread_default (async_context);
 	return NULL;
 }
 
@@ -208,7 +220,7 @@ test1_thread (gpointer user_data)
 static gboolean idle_test2_fail (gpointer user_data);
 
 static void
-do_test2 (void)
+do_test2 (int n, gboolean use_thread_context)
 {
 	guint idle;
 	GMainContext *async_context;
@@ -216,15 +228,25 @@ do_test2 (void)
 	char *uri;
 	SoupMessage *msg;
 
-	debug_printf (1, "Test 2: a session with its own context is independent of the main loop.\n");
+	debug_printf (1, "\nTest %d: a session with its own context is independent of the main loop.\n", n);
+	if (use_thread_context)
+		debug_printf (1, "(Using g_main_context_push_thread_default())\n");
+	else
+		debug_printf (1, "(Using SOUP_SESSION_ASYNC_CONTEXT)\n");
 
 	idle = g_idle_add_full (G_PRIORITY_HIGH, idle_test2_fail, NULL, NULL);
 
 	async_context = g_main_context_new ();
-	session = soup_test_session_new (
-		SOUP_TYPE_SESSION_ASYNC,
-		SOUP_SESSION_ASYNC_CONTEXT, async_context,
-		NULL);
+	if (use_thread_context) {
+		g_main_context_push_thread_default (async_context);
+		session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+						 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+						 NULL);
+	} else {
+		session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+						 SOUP_SESSION_ASYNC_CONTEXT, async_context,
+						 NULL);
+	}
 	g_main_context_unref (async_context);
 
 	uri = g_build_filename (base_uri, "slow", NULL);
@@ -243,6 +265,9 @@ do_test2 (void)
 	g_free (uri);
 
 	g_source_remove (idle);
+
+	if (use_thread_context)
+		g_main_context_pop_thread_default (async_context);
 }
 
 static gboolean
@@ -253,6 +278,110 @@ idle_test2_fail (gpointer user_data)
 	return FALSE;
 }
 
+static void
+multi_request_started (SoupSession *session, SoupMessage *msg,
+		       SoupSocket *socket, gpointer user_data)
+{
+	g_object_set_data (G_OBJECT (msg), "started", GUINT_TO_POINTER (TRUE));
+}
+
+static void
+msg1_got_headers (SoupMessage *msg, gpointer user_data)
+{
+	GMainLoop *loop = user_data;
+
+	g_main_loop_quit (loop);
+}
+
+static void
+multi_msg_finished (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+	GMainLoop *loop = user_data;
+
+	g_object_set_data (G_OBJECT (msg), "finished", GUINT_TO_POINTER (TRUE));
+	g_main_loop_quit (loop);
+}
+
+static void
+do_multicontext_test (int n)
+{
+	SoupSession *session;
+	SoupMessage *msg1, *msg2;
+	GMainContext *context1, *context2;
+	GMainLoop *loop1, *loop2;
+
+	debug_printf (1, "\nTest %d: Using multiple async contexts\n", n);
+
+	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+					 NULL);
+	g_signal_connect (session, "request-started",
+			  G_CALLBACK (multi_request_started), NULL);
+
+	context1 = g_main_context_new ();
+	loop1 = g_main_loop_new (context1, FALSE);
+	context2 = g_main_context_new ();
+	loop2 = g_main_loop_new (context2, FALSE);
+
+	g_main_context_push_thread_default (context1);
+	msg1 = soup_message_new ("GET", base_uri);
+	g_object_ref (msg1);
+	soup_session_queue_message (session, msg1, multi_msg_finished, loop1);
+	g_signal_connect (msg1, "got-headers",
+			  G_CALLBACK (msg1_got_headers), loop1);
+	g_object_set_data (G_OBJECT (msg1), "session", session);
+	g_main_context_pop_thread_default (context1);
+
+	g_main_context_push_thread_default (context2);
+	msg2 = soup_message_new ("GET", base_uri);
+	g_object_ref (msg2);
+	soup_session_queue_message (session, msg2, multi_msg_finished, loop2);
+	g_main_context_pop_thread_default (context2);
+
+	g_main_context_push_thread_default (context1);
+	g_main_loop_run (loop1);
+	g_main_context_pop_thread_default (context1);
+
+	if (!g_object_get_data (G_OBJECT (msg1), "started")) {
+		debug_printf (1, "  msg1 not started??\n");
+		errors++;
+	}
+	if (g_object_get_data (G_OBJECT (msg2), "started")) {
+		debug_printf (1, "  msg2 started while loop1 was running!\n");
+		errors++;
+	}
+
+	g_main_context_push_thread_default (context2);
+	g_main_loop_run (loop2);
+	g_main_context_pop_thread_default (context2);
+
+	if (g_object_get_data (G_OBJECT (msg1), "finished")) {
+		debug_printf (1, "  msg1 finished while loop2 was running!\n");
+		errors++;
+	}
+	if (!g_object_get_data (G_OBJECT (msg2), "finished")) {
+		debug_printf (1, "  msg2 not finished??\n");
+		errors++;
+	}
+
+	g_main_context_push_thread_default (context1);
+	g_main_loop_run (loop1);
+	g_main_context_pop_thread_default (context1);
+
+	if (!g_object_get_data (G_OBJECT (msg1), "finished")) {
+		debug_printf (1, "  msg1 not finished??\n");
+		errors++;
+	}
+
+	g_main_loop_unref (loop1);
+	g_main_loop_unref (loop2);
+	g_main_context_unref (context1);
+	g_main_context_unref (context2);
+	g_object_unref (msg1);
+	g_object_unref (msg2);
+
+	soup_test_session_abort_unref (session);
+}
 
 int
 main (int argc, char **argv)
@@ -266,8 +395,11 @@ main (int argc, char **argv)
 	base_uri = g_strdup_printf ("http://127.0.0.1:%u/";,
 				    soup_server_get_port (server));
 
-	do_test1 ();
-	do_test2 ();
+	do_test1 (1, FALSE);
+	do_test1 (2, TRUE);
+	do_test2 (3, FALSE);
+	do_test2 (4, TRUE);
+	do_multicontext_test (5);
 
 	g_free (base_uri);
 	soup_test_server_quit_unref (server);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]