1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2000-2003, Ximian, Inc.
12 #include "soup-address.h"
13 #include "soup-session-sync.h"
14 #include "soup-session-private.h"
15 #include "soup-address.h"
16 #include "soup-message-private.h"
17 #include "soup-misc.h"
20 * SECTION:soup-session-sync
21 * @short_description: Soup session for blocking I/O in multithreaded
24 * #SoupSessionSync is an implementation of #SoupSession that uses
25 * synchronous I/O, intended for use in multi-threaded programs.
27 * You can use #SoupSessionSync from multiple threads concurrently.
28 * Eg, you can send a #SoupMessage in one thread, and then while
29 * waiting for the response, send another #SoupMessage from another
30 * thread. You can also send a message from one thread and then call
31 * soup_session_cancel_message() on it from any other thread (although
32 * you need to be careful to avoid race conditions, where the message
33 * finishes and is then unreffed by the sending thread just before you
36 * However, the majority of other types and methods in libsoup are not
37 * MT-safe. In particular, you <emphasis>cannot</emphasis> modify or
38 * examine a #SoupMessage while it is being transmitted by
39 * #SoupSessionSync in another thread. Once a message has been handed
40 * off to #SoupSessionSync, it can only be manipulated from its signal
41 * handler callbacks, until I/O is complete.
47 } SoupSessionSyncPrivate;
48 #define SOUP_SESSION_SYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncPrivate))
50 static void queue_message (SoupSession *session, SoupMessage *msg,
51 SoupSessionCallback callback, gpointer user_data);
52 static guint send_message (SoupSession *session, SoupMessage *msg);
53 static void cancel_message (SoupSession *session, SoupMessage *msg,
56 G_DEFINE_TYPE (SoupSessionSync, soup_session_sync, SOUP_TYPE_SESSION)
59 soup_session_sync_init (SoupSessionSync *ss)
61 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (ss);
63 priv->lock = g_mutex_new ();
64 priv->cond = g_cond_new ();
68 finalize (GObject *object)
70 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (object);
72 g_mutex_free (priv->lock);
73 g_cond_free (priv->cond);
75 G_OBJECT_CLASS (soup_session_sync_parent_class)->finalize (object);
79 soup_session_sync_class_init (SoupSessionSyncClass *session_sync_class)
81 GObjectClass *object_class = G_OBJECT_CLASS (session_sync_class);
82 SoupSessionClass *session_class = SOUP_SESSION_CLASS (session_sync_class);
84 g_type_class_add_private (session_sync_class, sizeof (SoupSessionSyncPrivate));
86 /* virtual method override */
87 session_class->queue_message = queue_message;
88 session_class->send_message = send_message;
89 session_class->cancel_message = cancel_message;
90 object_class->finalize = finalize;
95 * soup_session_sync_new:
97 * Creates an synchronous #SoupSession with the default options.
99 * Return value: the new session.
102 soup_session_sync_new (void)
104 return g_object_new (SOUP_TYPE_SESSION_SYNC, NULL);
108 * soup_session_sync_new_with_options:
109 * @optname1: name of first property to set
110 * @...: value of @optname1, followed by additional property/value pairs
112 * Creates an synchronous #SoupSession with the specified options.
114 * Return value: the new session.
117 soup_session_sync_new_with_options (const char *optname1, ...)
119 SoupSession *session;
122 va_start (ap, optname1);
123 session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_SYNC,
130 static SoupConnection *
131 wait_for_connection (SoupSession *session, SoupMessage *msg)
133 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
134 gboolean try_pruning = FALSE, is_new = FALSE;
135 SoupProxyResolver *proxy_resolver;
136 SoupAddress *proxy_addr = NULL;
137 SoupConnection *conn;
140 proxy_resolver = soup_session_get_proxy_resolver (session);
141 g_mutex_lock (priv->lock);
144 if (proxy_resolver) {
145 status = soup_proxy_resolver_get_proxy_sync (proxy_resolver, msg, NULL, &proxy_addr);
146 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
147 g_mutex_unlock (priv->lock);
148 soup_session_cancel_message (session, msg, status);
153 conn = soup_session_get_connection (session, msg, proxy_addr,
154 &try_pruning, &is_new);
156 g_object_unref (proxy_addr);
159 status = soup_connection_connect_sync (conn);
161 /* If the connection attempt fails, SoupSession
162 * will notice, unref conn, and set an error
163 * status on msg. So all we need to do is just
164 * not return the no-longer-valid connection.
167 if (status == SOUP_STATUS_TRY_AGAIN)
169 else if (!SOUP_STATUS_IS_SUCCESSFUL (status))
171 else if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
172 /* Message was cancelled while we were
175 soup_connection_disconnect (conn);
180 g_mutex_unlock (priv->lock);
184 if (try_pruning && soup_session_try_prune_connection (session))
188 g_cond_wait (priv->cond, priv->lock);
190 /* See if something bad happened */
191 if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
192 g_mutex_unlock (priv->lock);
200 process_queue_item (SoupMessageQueueItem *item)
202 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (item->session);
203 SoupMessage *msg = item->msg;
204 SoupConnection *conn;
209 /* Resolve address */
210 addr = soup_message_get_address (msg);
211 status = soup_address_resolve_sync (addr, item->cancellable);
212 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
213 if (status != SOUP_STATUS_CANCELLED)
214 soup_session_cancel_message (item->session, msg, status);
218 /* Get a connection */
219 conn = wait_for_connection (item->session, msg);
223 soup_connection_send_request (conn, msg);
224 g_cond_broadcast (priv->cond);
225 } while (soup_message_get_io_status (msg) != SOUP_MESSAGE_IO_STATUS_FINISHED);
229 queue_message_callback (gpointer data)
231 SoupMessageQueueItem *item = data;
233 item->callback (item->session, item->msg, item->callback_data);
234 g_object_unref (item->session);
235 soup_message_queue_item_unref (item);
240 queue_message_thread (gpointer data)
242 SoupMessageQueueItem *item = data;
244 process_queue_item (item);
245 if (item->callback) {
246 soup_add_completion (soup_session_get_async_context (item->session),
247 queue_message_callback, item);
249 g_object_unref (item->session);
250 soup_message_queue_item_unref (item);
257 queue_message (SoupSession *session, SoupMessage *msg,
258 SoupSessionCallback callback, gpointer user_data)
260 SoupMessageQueueItem *item;
262 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
263 queue_message (g_object_ref (session), msg, callback, user_data);
265 item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
266 g_return_if_fail (item != NULL);
268 g_thread_create (queue_message_thread, item, FALSE, NULL);
272 send_message (SoupSession *session, SoupMessage *msg)
274 SoupMessageQueueItem *item;
277 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->queue_message (session, msg, NULL, NULL);
279 item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
280 g_return_val_if_fail (item != NULL, SOUP_STATUS_MALFORMED);
282 process_queue_item (item);
283 status = msg->status_code;
284 soup_message_queue_item_unref (item);
289 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
291 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
293 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->cancel_message (session, msg, status_code);
294 g_cond_broadcast (priv->cond);