[libsoup] soup-session: handle cancellation of SoupCache resources



commit 52b4df4b2b6d2c70fa333ce712041fd85a65d7c9
Author: Sergio Villar Senin <svillar igalia com>
Date:   Wed Jan 30 20:05:29 2013 +0100

    soup-session: handle cancellation of SoupCache resources
    
    SoupSession will now properly handle cancellations (either with
    soup_session_cancel_message or g_cancellable_cancel) of resources returned
    by the cache whether they're fresh or need a revalidation.
    
    http://bugzilla.gnome.org/show_bug.cgi?id=692310

 libsoup/soup-cache-private.h |    2 +
 libsoup/soup-cache.c         |   13 ++++++
 libsoup/soup-session.c       |   94 +++++++++++++++++++++++++++++++-----------
 3 files changed, 85 insertions(+), 24 deletions(-)
---
diff --git a/libsoup/soup-cache-private.h b/libsoup/soup-cache-private.h
index 3843e8e..65ad868 100644
--- a/libsoup/soup-cache-private.h
+++ b/libsoup/soup-cache-private.h
@@ -36,6 +36,8 @@ SoupCacheability   soup_cache_get_cacheability             (SoupCache   *cache,
 							    SoupMessage *msg);
 SoupMessage       *soup_cache_generate_conditional_request (SoupCache   *cache,
 							    SoupMessage *original);
+void               soup_cache_cancel_conditional_request   (SoupCache   *cache,
+							    SoupMessage *msg);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-cache.c b/libsoup/soup-cache.c
index 5569d6b..839cdc1 100644
--- a/libsoup/soup-cache.c
+++ b/libsoup/soup-cache.c
@@ -1377,6 +1377,19 @@ soup_cache_generate_conditional_request (SoupCache *cache, SoupMessage *original
 	return msg;
 }
 
+void
+soup_cache_cancel_conditional_request (SoupCache   *cache,
+				       SoupMessage *msg)
+{
+	SoupCacheEntry *entry;
+
+	entry = soup_cache_entry_lookup (cache, msg);
+	if (entry)
+		entry->being_validated = FALSE;
+
+	soup_session_cancel_message (cache->priv->session, msg, SOUP_STATUS_CANCELLED);
+}
+
 static void
 pack_entry (gpointer data,
 	    gpointer user_data)
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index c50faa2..ea0a16b 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -3781,12 +3781,49 @@ async_return_from_cache (SoupMessageQueueItem *item,
 	async_send_request_return_result (item, g_object_ref (stream), NULL);
 }
 
+typedef struct {
+	SoupCache *cache;
+	SoupMessage *conditional_msg;
+} AsyncCacheCancelData;
+
+
+static void
+free_async_cache_cancel_data (AsyncCacheCancelData *data)
+{
+	g_object_unref (data->conditional_msg);
+	g_object_unref (data->cache);
+	g_slice_free (AsyncCacheCancelData, data);
+}
+
+static void
+cancel_cache_response (SoupMessageQueueItem *item)
+{
+	item->paused = FALSE;
+	item->state = SOUP_MESSAGE_FINISHING;
+	soup_message_set_status (item->msg, SOUP_STATUS_CANCELLED);
+	soup_session_kick_queue (item->session);
+}
+
+static void
+conditional_request_cancelled_cb (GCancellable *cancellable, AsyncCacheCancelData *data)
+{
+	soup_cache_cancel_conditional_request (data->cache, data->conditional_msg);
+}
+
 static void
 conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
 {
 	SoupMessageQueueItem *item = user_data;
 	GInputStream *stream;
 
+	if (g_cancellable_is_cancelled (item->cancellable)) {
+		cancel_cache_response (item);
+		return;
+	} else {
+		gulong handler_id = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (msg), "SoupSession:handler-id"));
+		g_cancellable_disconnect (item->cancellable, handler_id);
+	}
+
 	if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
 		SoupCache *cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
 
@@ -3805,29 +3842,32 @@ conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_
 	soup_session_kick_queue (session);
 }
 
-typedef struct {
-	SoupMessageQueueItem *item;
-	GInputStream *stream;
-} SendAsyncCacheData;
-
-static void
-free_send_async_cache_data (SendAsyncCacheData *sacd)
-{
-	soup_message_queue_item_unref (sacd->item);
-	g_object_unref (sacd->stream);
-	g_slice_free (SendAsyncCacheData, sacd);
-}
-
 static gboolean
 idle_return_from_cache_cb (gpointer data)
 {
 	GTask *task = data;
-	SendAsyncCacheData *sacd = g_task_get_task_data (task);
+	SoupMessageQueueItem *item = g_task_get_task_data (task);
+	GInputStream *istream;
+
+	if (item->state == SOUP_MESSAGE_FINISHED) {
+		/* The original request was cancelled using
+		 * soup_session_cancel_message () so it has been
+		 * already handled by the cancellation code path.
+		 */
+		return FALSE;
+	} else if (g_cancellable_is_cancelled (item->cancellable)) {
+		/* Cancel original msg after g_cancellable_cancel(). */
+		cancel_cache_response (item);
+		return FALSE;
+	}
+
+	istream = g_object_steal_data (G_OBJECT (task), "SoupSession:istream");
+	async_return_from_cache (item, istream);
 
-	async_return_from_cache (sacd->item, sacd->stream);
 	return FALSE;
 }
 
+
 static gboolean
 async_respond_from_cache (SoupSession          *session,
 			  SoupMessageQueueItem *item)
@@ -3842,7 +3882,6 @@ async_respond_from_cache (SoupSession          *session,
 	response = soup_cache_has_response (cache, item->msg);
 	if (response == SOUP_CACHE_RESPONSE_FRESH) {
 		GInputStream *stream;
-		SendAsyncCacheData *sacd;
 		GSource *source;
 
 		stream = soup_cache_send_response (cache, item->msg);
@@ -3850,14 +3889,8 @@ async_respond_from_cache (SoupSession          *session,
 			/* Cached file was deleted? */
 			return FALSE;
 		}
-
-		sacd = g_slice_new (SendAsyncCacheData);
-		sacd->item = item;
-		soup_message_queue_item_ref (item);
-		sacd->stream = stream;
-
-		g_task_set_task_data (item->task, sacd,
-				      (GDestroyNotify) free_send_async_cache_data);
+		g_object_set_data_full (G_OBJECT (item->task), "SoupSession:istream",
+					stream, g_object_unref);
 
 		source = g_timeout_source_new (0);
 		g_task_attach_source (item->task, source,
@@ -3866,14 +3899,27 @@ async_respond_from_cache (SoupSession          *session,
 		return TRUE;
 	} else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
 		SoupMessage *conditional_msg;
+		AsyncCacheCancelData *data;
+		gulong handler_id;
 
 		conditional_msg = soup_cache_generate_conditional_request (cache, item->msg);
 		if (!conditional_msg)
 			return FALSE;
 
+		/* Detect any quick cancellation before the cache is able to return data. */
+		data = g_slice_new0 (AsyncCacheCancelData);
+		data->cache = g_object_ref (cache);
+		data->conditional_msg = g_object_ref (conditional_msg);
+		handler_id = g_cancellable_connect (item->cancellable, G_CALLBACK (conditional_request_cancelled_cb),
+						    data, (GDestroyNotify) free_async_cache_cancel_data);
+
+		g_object_set_data (G_OBJECT (conditional_msg), "SoupSession:handler-id",
+				   GSIZE_TO_POINTER (handler_id));
 		soup_session_queue_message (session, conditional_msg,
 					    conditional_get_ready_cb,
 					    item);
+
+
 		return TRUE;
 	} else
 		return FALSE;


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