Blame libsoup/soup-message-queue.c

Packit Service ca3877
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
Packit Service ca3877
/*
Packit Service ca3877
 * soup-message-queue.c: Message queue
Packit Service ca3877
 *
Packit Service ca3877
 * Copyright (C) 2003 Novell, Inc.
Packit Service ca3877
 * Copyright (C) 2008 Red Hat, Inc.
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
#ifdef HAVE_CONFIG_H
Packit Service ca3877
#include <config.h>
Packit Service ca3877
#endif
Packit Service ca3877
Packit Service ca3877
#include "soup-message-queue.h"
Packit Service ca3877
#include "soup.h"
Packit Service ca3877
Packit Service ca3877
/* This is an internal structure used by #SoupSession and its
Packit Service ca3877
 * subclasses to keep track of the status of messages currently being
Packit Service ca3877
 * processed.
Packit Service ca3877
 *
Packit Service ca3877
 * The #SoupMessageQueue itself is mostly just a linked list of
Packit Service ca3877
 * #SoupMessageQueueItem, with some added cleverness to allow the list
Packit Service ca3877
 * to be walked safely while other threads / re-entrant loops are
Packit Service ca3877
 * adding items to and removing items from it. In particular, this is
Packit Service ca3877
 * handled by refcounting items and then keeping "removed" items in
Packit Service ca3877
 * the list until their ref_count drops to 0, but skipping over the
Packit Service ca3877
 * "removed" ones when walking the queue.
Packit Service ca3877
 **/
Packit Service ca3877
Packit Service ca3877
struct _SoupMessageQueue {
Packit Service ca3877
	SoupSession *session;
Packit Service ca3877
Packit Service ca3877
	GMutex mutex;
Packit Service ca3877
	SoupMessageQueueItem *head, *tail;
Packit Service ca3877
};
Packit Service ca3877
Packit Service ca3877
SoupMessageQueue *
Packit Service ca3877
soup_message_queue_new (SoupSession *session)
Packit Service ca3877
{
Packit Service ca3877
	SoupMessageQueue *queue;
Packit Service ca3877
Packit Service ca3877
	queue = g_slice_new0 (SoupMessageQueue);
Packit Service ca3877
	queue->session = session;
Packit Service ca3877
	g_mutex_init (&queue->mutex);
Packit Service ca3877
	return queue;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
void
Packit Service ca3877
soup_message_queue_destroy (SoupMessageQueue *queue)
Packit Service ca3877
{
Packit Service ca3877
	g_return_if_fail (queue->head == NULL);
Packit Service ca3877
Packit Service ca3877
	g_mutex_clear (&queue->mutex);
Packit Service ca3877
	g_slice_free (SoupMessageQueue, queue);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
queue_message_restarted (SoupMessage *msg, gpointer user_data)
Packit Service ca3877
{
Packit Service ca3877
	SoupMessageQueueItem *item = user_data;
Packit Service ca3877
Packit Service ca3877
	g_cancellable_reset (item->cancellable);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_message_queue_append:
Packit Service ca3877
 * @queue: a #SoupMessageQueue
Packit Service ca3877
 * @msg: a #SoupMessage
Packit Service ca3877
 * @callback: the callback for @msg
Packit Service ca3877
 * @user_data: the data to pass to @callback
Packit Service ca3877
 *
Packit Service ca3877
 * Creates a new #SoupMessageQueueItem and appends it to @queue.
Packit Service ca3877
 *
Packit Service ca3877
 * Return value: the new item, which you must unref with
Packit Service ca3877
 * soup_message_queue_unref_item() when you are done with.
Packit Service ca3877
 **/
Packit Service ca3877
SoupMessageQueueItem *
Packit Service ca3877
soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
Packit Service ca3877
			   SoupSessionCallback callback, gpointer user_data)
Packit Service ca3877
{
Packit Service ca3877
	SoupMessageQueueItem *item;
Packit Service ca3877
Packit Service ca3877
	item = g_slice_new0 (SoupMessageQueueItem);
Packit Service ca3877
	item->session = g_object_ref (queue->session);
Packit Service ca3877
	item->async_context = soup_session_get_async_context (item->session);
Packit Service ca3877
	if (item->async_context)
Packit Service ca3877
		g_main_context_ref (item->async_context);
Packit Service ca3877
	item->queue = queue;
Packit Service ca3877
	item->msg = g_object_ref (msg);
Packit Service ca3877
	item->callback = callback;
Packit Service ca3877
	item->callback_data = user_data;
Packit Service ca3877
	item->cancellable = g_cancellable_new ();
Packit Service ca3877
	item->priority = soup_message_get_priority (msg);
Packit Service ca3877
Packit Service ca3877
	g_signal_connect (msg, "restarted",
Packit Service ca3877
			  G_CALLBACK (queue_message_restarted), item);
Packit Service ca3877
Packit Service ca3877
	/* Note: the initial ref_count of 1 represents the caller's
Packit Service ca3877
	 * ref; the queue's own ref is indicated by the absence of the
Packit Service ca3877
	 * "removed" flag.
Packit Service ca3877
	 */
Packit Service ca3877
	item->ref_count = 1;
Packit Service ca3877
Packit Service ca3877
	g_mutex_lock (&queue->mutex);
Packit Service ca3877
	if (queue->head) {
Packit Service ca3877
		SoupMessageQueueItem *it = queue->head;
Packit Service ca3877
Packit Service ca3877
		while (it && it->priority >= item->priority)
Packit Service ca3877
			it = it->next;
Packit Service ca3877
Packit Service ca3877
		if (!it) {
Packit Service ca3877
			if (queue->tail) {
Packit Service ca3877
				queue->tail->next = item;
Packit Service ca3877
				item->prev = queue->tail;
Packit Service ca3877
			} else
Packit Service ca3877
				queue->head = item;
Packit Service ca3877
			queue->tail = item;
Packit Service ca3877
		} else {
Packit Service ca3877
			if (it != queue->head)
Packit Service ca3877
				it->prev->next = item;
Packit Service ca3877
			else
Packit Service ca3877
				queue->head = item;
Packit Service ca3877
			item->prev = it->prev;
Packit Service ca3877
			it->prev = item;
Packit Service ca3877
			item->next = it;
Packit Service ca3877
		}
Packit Service ca3877
	} else
Packit Service ca3877
		queue->head = queue->tail = item;
Packit Service ca3877
Packit Service ca3877
	g_mutex_unlock (&queue->mutex);
Packit Service ca3877
	return item;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_message_queue_item_ref:
Packit Service ca3877
 * @item: a #SoupMessageQueueItem
Packit Service ca3877
 *
Packit Service ca3877
 * Refs @item.
Packit Service ca3877
 **/ 
Packit Service ca3877
void
Packit Service ca3877
soup_message_queue_item_ref (SoupMessageQueueItem *item)
Packit Service ca3877
{
Packit Service ca3877
	g_mutex_lock (&item->queue->mutex);
Packit Service ca3877
	item->ref_count++;
Packit Service ca3877
	g_mutex_unlock (&item->queue->mutex);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_message_queue_item_unref:
Packit Service ca3877
 * @item: a #SoupMessageQueueItem
Packit Service ca3877
 *
Packit Service ca3877
 * Unrefs @item; use this on a #SoupMessageQueueItem that you are done
Packit Service ca3877
 * with (but that you aren't passing to
Packit Service ca3877
 * soup_message_queue_item_next()).
Packit Service ca3877
 **/ 
Packit Service ca3877
void
Packit Service ca3877
soup_message_queue_item_unref (SoupMessageQueueItem *item)
Packit Service ca3877
{
Packit Service ca3877
	g_mutex_lock (&item->queue->mutex);
Packit Service ca3877
Packit Service ca3877
	/* Decrement the ref_count; if it's still non-zero OR if the
Packit Service ca3877
	 * item is still in the queue, then return.
Packit Service ca3877
	 */
Packit Service ca3877
	if (--item->ref_count || !item->removed) {
Packit Service ca3877
		g_mutex_unlock (&item->queue->mutex);
Packit Service ca3877
		return;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	g_warn_if_fail (item->conn == NULL);
Packit Service ca3877
Packit Service ca3877
	/* OK, @item is dead. Rewrite @queue around it */
Packit Service ca3877
	if (item->prev)
Packit Service ca3877
		item->prev->next = item->next;
Packit Service ca3877
	else
Packit Service ca3877
		item->queue->head = item->next;
Packit Service ca3877
	if (item->next)
Packit Service ca3877
		item->next->prev = item->prev;
Packit Service ca3877
	else
Packit Service ca3877
		item->queue->tail = item->prev;
Packit Service ca3877
Packit Service ca3877
	g_mutex_unlock (&item->queue->mutex);
Packit Service ca3877
Packit Service ca3877
	/* And free it */
Packit Service ca3877
	g_signal_handlers_disconnect_by_func (item->msg,
Packit Service ca3877
					      queue_message_restarted, item);
Packit Service ca3877
	g_object_unref (item->session);
Packit Service ca3877
	g_object_unref (item->msg);
Packit Service ca3877
	g_object_unref (item->cancellable);
Packit Service ca3877
	g_clear_error (&item->error);
Packit Service ca3877
	g_clear_object (&item->task);
Packit Service ca3877
	g_clear_pointer (&item->async_context, g_main_context_unref);
Packit Service ca3877
	if (item->io_source) {
Packit Service ca3877
		g_source_destroy (item->io_source);
Packit Service ca3877
		g_source_unref (item->io_source);
Packit Service ca3877
	}
Packit Service ca3877
	g_slice_free (SoupMessageQueueItem, item);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_message_queue_lookup:
Packit Service ca3877
 * @queue: a #SoupMessageQueue
Packit Service ca3877
 * @msg: a #SoupMessage
Packit Service ca3877
 *
Packit Service ca3877
 * Finds the #SoupMessageQueueItem for @msg in @queue. You must unref
Packit Service ca3877
 * the item with soup_message_queue_unref_item() when you are done
Packit Service ca3877
 * with it.
Packit Service ca3877
 *
Packit Service ca3877
 * Return value: (nullable): the queue item for @msg, or %NULL
Packit Service ca3877
 **/ 
Packit Service ca3877
SoupMessageQueueItem *
Packit Service ca3877
soup_message_queue_lookup (SoupMessageQueue *queue, SoupMessage *msg)
Packit Service ca3877
{
Packit Service ca3877
	SoupMessageQueueItem *item;
Packit Service ca3877
Packit Service ca3877
	g_mutex_lock (&queue->mutex);
Packit Service ca3877
Packit Service ca3877
	item = queue->tail;
Packit Service ca3877
	while (item && (item->removed || item->msg != msg))
Packit Service ca3877
		item = item->prev;
Packit Service ca3877
Packit Service ca3877
	if (item)
Packit Service ca3877
		item->ref_count++;
Packit Service ca3877
Packit Service ca3877
	g_mutex_unlock (&queue->mutex);
Packit Service ca3877
	return item;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_message_queue_first:
Packit Service ca3877
 * @queue: a #SoupMessageQueue
Packit Service ca3877
 *
Packit Service ca3877
 * Gets the first item in @queue. You must unref the item by calling
Packit Service ca3877
 * soup_message_queue_unref_item() on it when you are done.
Packit Service ca3877
 * (soup_message_queue_next() does this for you automatically, so you
Packit Service ca3877
 * only need to unref the item yourself if you are not going to
Packit Service ca3877
 * finishing walking the queue.)
Packit Service ca3877
 *
Packit Service ca3877
 * Return value: the first item in @queue.
Packit Service ca3877
 **/ 
Packit Service ca3877
SoupMessageQueueItem *
Packit Service ca3877
soup_message_queue_first (SoupMessageQueue *queue)
Packit Service ca3877
{
Packit Service ca3877
	SoupMessageQueueItem *item;
Packit Service ca3877
Packit Service ca3877
	g_mutex_lock (&queue->mutex);
Packit Service ca3877
Packit Service ca3877
	item = queue->head;
Packit Service ca3877
	while (item && item->removed)
Packit Service ca3877
		item = item->next;
Packit Service ca3877
Packit Service ca3877
	if (item)
Packit Service ca3877
		item->ref_count++;
Packit Service ca3877
Packit Service ca3877
	g_mutex_unlock (&queue->mutex);
Packit Service ca3877
	return item;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_message_queue_next:
Packit Service ca3877
 * @queue: a #SoupMessageQueue
Packit Service ca3877
 * @item: a #SoupMessageQueueItem
Packit Service ca3877
 *
Packit Service ca3877
 * Unrefs @item and gets the next item after it in @queue. As with
Packit Service ca3877
 * soup_message_queue_first(), you must unref the returned item
Packit Service ca3877
 * yourself with soup_message_queue_unref_item() if you do not finish
Packit Service ca3877
 * walking the queue.
Packit Service ca3877
 *
Packit Service ca3877
 * Return value: the next item in @queue.
Packit Service ca3877
 **/ 
Packit Service ca3877
SoupMessageQueueItem *
Packit Service ca3877
soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueItem *item)
Packit Service ca3877
{
Packit Service ca3877
	SoupMessageQueueItem *next;
Packit Service ca3877
Packit Service ca3877
	g_mutex_lock (&queue->mutex);
Packit Service ca3877
Packit Service ca3877
	next = item->next;
Packit Service ca3877
	while (next && next->removed)
Packit Service ca3877
		next = next->next;
Packit Service ca3877
	if (next)
Packit Service ca3877
		next->ref_count++;
Packit Service ca3877
Packit Service ca3877
	g_mutex_unlock (&queue->mutex);
Packit Service ca3877
	soup_message_queue_item_unref (item);
Packit Service ca3877
	return next;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_message_queue_remove:
Packit Service ca3877
 * @queue: a #SoupMessageQueue
Packit Service ca3877
 * @item: a #SoupMessageQueueItem
Packit Service ca3877
 *
Packit Service ca3877
 * Removes @item from @queue. Note that you probably also need to call
Packit Service ca3877
 * soup_message_queue_unref_item() after this.
Packit Service ca3877
 **/ 
Packit Service ca3877
void
Packit Service ca3877
soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueItem *item)
Packit Service ca3877
{
Packit Service ca3877
	g_return_if_fail (!item->removed);
Packit Service ca3877
Packit Service ca3877
	g_mutex_lock (&queue->mutex);
Packit Service ca3877
	item->removed = TRUE;
Packit Service ca3877
	g_mutex_unlock (&queue->mutex);
Packit Service ca3877
}