1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2000-2003, Ximian, Inc.
12 #include "soup-session-sync.h"
13 #include "soup-session-private.h"
14 #include "soup-address.h"
15 #include "soup-message-private.h"
16 #include "soup-misc.h"
19 * SECTION:soup-session-sync
20 * @short_description: Soup session for blocking I/O in multithreaded
23 * #SoupSessionSync is an implementation of #SoupSession that uses
24 * synchronous I/O, intended for use in multi-threaded programs.
26 * You can use #SoupSessionSync from multiple threads concurrently.
27 * Eg, you can send a #SoupMessage in one thread, and then while
28 * waiting for the response, send another #SoupMessage from another
29 * thread. You can also send a message from one thread and then call
30 * soup_session_cancel_message() on it from any other thread (although
31 * you need to be careful to avoid race conditions, where the message
32 * finishes and is then unreffed by the sending thread just before you
35 * However, the majority of other types and methods in libsoup are not
36 * MT-safe. In particular, you <emphasis>cannot</emphasis> modify or
37 * examine a #SoupMessage while it is being transmitted by
38 * #SoupSessionSync in another thread. Once a message has been handed
39 * off to #SoupSessionSync, it can only be manipulated from its signal
40 * handler callbacks, until I/O is complete.
46 } SoupSessionSyncPrivate;
47 #define SOUP_SESSION_SYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncPrivate))
49 static void queue_message (SoupSession *session, SoupMessage *msg,
50 SoupSessionCallback callback, gpointer user_data);
51 static guint send_message (SoupSession *session, SoupMessage *msg);
52 static void cancel_message (SoupSession *session, SoupMessage *msg,
55 G_DEFINE_TYPE (SoupSessionSync, soup_session_sync, SOUP_TYPE_SESSION)
58 soup_session_sync_init (SoupSessionSync *ss)
60 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (ss);
62 priv->lock = g_mutex_new ();
63 priv->cond = g_cond_new ();
67 finalize (GObject *object)
69 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (object);
71 g_mutex_free (priv->lock);
72 g_cond_free (priv->cond);
74 G_OBJECT_CLASS (soup_session_sync_parent_class)->finalize (object);
78 soup_session_sync_class_init (SoupSessionSyncClass *session_sync_class)
80 GObjectClass *object_class = G_OBJECT_CLASS (session_sync_class);
81 SoupSessionClass *session_class = SOUP_SESSION_CLASS (session_sync_class);
83 g_type_class_add_private (session_sync_class, sizeof (SoupSessionSyncPrivate));
85 /* virtual method override */
86 session_class->queue_message = queue_message;
87 session_class->send_message = send_message;
88 session_class->cancel_message = cancel_message;
89 object_class->finalize = finalize;
94 * soup_session_sync_new:
96 * Creates an synchronous #SoupSession with the default options.
98 * Return value: the new session.
101 soup_session_sync_new (void)
103 return g_object_new (SOUP_TYPE_SESSION_SYNC, NULL);
107 * soup_session_sync_new_with_options:
108 * @optname1: name of first property to set
109 * @...: value of @optname1, followed by additional property/value pairs
111 * Creates an synchronous #SoupSession with the specified options.
113 * Return value: the new session.
116 soup_session_sync_new_with_options (const char *optname1, ...)
118 SoupSession *session;
121 va_start (ap, optname1);
122 session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_SYNC,
129 static SoupConnection *
130 wait_for_connection (SoupSession *session, SoupMessage *msg)
132 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
133 SoupConnection *conn;
134 gboolean try_pruning = FALSE, is_new = FALSE;
137 g_mutex_lock (priv->lock);
140 conn = soup_session_get_connection (session, msg,
141 &try_pruning, &is_new);
144 status = soup_connection_connect_sync (conn);
146 /* If the connection attempt fails, SoupSession
147 * will notice, unref conn, and set an error
148 * status on msg. So all we need to do is just
149 * not return the no-longer-valid connection.
152 if (status == SOUP_STATUS_TRY_AGAIN)
154 else if (!SOUP_STATUS_IS_SUCCESSFUL (status))
156 else if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
157 /* Message was cancelled while we were
160 soup_connection_disconnect (conn);
165 g_mutex_unlock (priv->lock);
169 if (try_pruning && soup_session_try_prune_connection (session))
173 g_cond_wait (priv->cond, priv->lock);
175 /* See if something bad happened */
176 if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
177 g_mutex_unlock (priv->lock);
185 process_queue_item (SoupMessageQueueItem *item)
187 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (item->session);
188 SoupMessage *msg = item->msg;
189 SoupConnection *conn;
194 /* Resolve address */
195 addr = soup_message_get_address (msg);
196 status = soup_address_resolve_sync (addr, item->cancellable);
197 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
198 if (status != SOUP_STATUS_CANCELLED)
199 soup_session_cancel_message (item->session, msg, status);
203 /* Get a connection */
204 conn = wait_for_connection (item->session, msg);
208 soup_connection_send_request (conn, msg);
209 g_cond_broadcast (priv->cond);
210 } while (soup_message_get_io_status (msg) != SOUP_MESSAGE_IO_STATUS_FINISHED);
212 soup_message_queue_remove (item->queue, item);
216 queue_message_callback (gpointer data)
218 SoupMessageQueueItem *item = data;
220 item->callback (item->session, item->msg, item->callback_data);
221 g_object_unref (item->session);
222 soup_message_queue_item_unref (item);
227 queue_message_thread (gpointer data)
229 SoupMessageQueueItem *item = data;
231 process_queue_item (item);
232 if (item->callback) {
233 soup_add_completion (soup_session_get_async_context (item->session),
234 queue_message_callback, item);
236 g_object_unref (item->session);
237 soup_message_queue_item_unref (item);
244 queue_message (SoupSession *session, SoupMessage *msg,
245 SoupSessionCallback callback, gpointer user_data)
247 SoupMessageQueueItem *item;
249 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
250 queue_message (g_object_ref (session), msg, callback, user_data);
252 item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
253 g_return_if_fail (item != NULL);
255 g_thread_create (queue_message_thread, item, FALSE, NULL);
259 send_message (SoupSession *session, SoupMessage *msg)
261 SoupMessageQueueItem *item;
264 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->queue_message (session, msg, NULL, NULL);
266 item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
267 g_return_val_if_fail (item != NULL, SOUP_STATUS_MALFORMED);
269 process_queue_item (item);
270 status = msg->status_code;
271 soup_message_queue_item_unref (item);
276 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
278 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
280 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->cancel_message (session, msg, status_code);
281 g_cond_broadcast (priv->cond);