GTlsDatabase *tlsdb;
gboolean ssl, ssl_strict, ssl_fallback;
- GMainContext *async_context;
+ GMainContext *async_context;
+ gboolean use_thread_context;
SoupMessageQueueItem *cur_item;
SoupConnectionState state;
PROP_SSL_STRICT,
PROP_SSL_FALLBACK,
PROP_ASYNC_CONTEXT,
+ PROP_USE_THREAD_CONTEXT,
PROP_TIMEOUT,
PROP_IDLE_TIMEOUT,
PROP_STATE,
"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",
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;
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;
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);
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);
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;
}
#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"
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;
SoupMessage *msg;
SoupSessionCallback callback;
gpointer callback_data;
+ GMainContext *async_context;
GCancellable *cancellable;
SoupAddress *proxy_addr;
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
{
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);
}
SoupSession *session = item->session;
SoupProxyURIResolver *proxy_resolver;
+ if (item->async_context != soup_session_get_async_context (session))
+ return;
+
do {
if (item->paused)
return;
{
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;
}
{
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));
}
}
GMutex *host_lock;
GMainContext *async_context;
+ gboolean use_thread_context;
GResolver *resolver;
PROP_TLS_DATABASE,
PROP_SSL_STRICT,
PROP_ASYNC_CONTEXT,
+ PROP_USE_THREAD_CONTEXT,
PROP_TIMEOUT,
PROP_USER_AGENT,
PROP_ACCEPT_LANGUAGE,
*
* 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,
"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
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;
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;
* 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
**/
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 */
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,
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);
}
#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"
PROP_SSL_STRICT,
PROP_SSL_FALLBACK,
PROP_ASYNC_CONTEXT,
+ PROP_USE_THREAD_CONTEXT,
PROP_TIMEOUT,
PROP_TRUSTED_CERTIFICATE,
PROP_CLEAN_DISPOSE,
guint ssl_strict:1;
guint ssl_fallback:1;
guint clean_dispose:1;
+ guint use_thread_context:1;
gpointer ssl_creds;
GMainContext *async_context;
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
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)
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;
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),
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 ();
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;
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;
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),
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,
#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"
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;
}
g_mutex_unlock (test1_mutex);
- g_main_loop_quit (loop);
+ g_main_loop_quit (test1_loop);
return FALSE;
}
}
static gpointer
-test1_thread (gpointer user_data)
+test1_thread (gpointer use_thread_context)
{
SoupSession *session;
GMainContext *async_context;
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);
g_free (uri);
g_cond_signal (test1_cond);
+
+ if (use_thread_context)
+ g_main_context_pop_thread_default (async_context);
return NULL;
}
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;
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);
g_free (uri);
g_source_remove (idle);
+
+ if (use_thread_context)
+ g_main_context_pop_thread_default (async_context);
}
static gboolean
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)
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);