libtotem-pl-parser/libtotem-pl-parser-2.24.3-add-asynchronous-parsing-functions.patch

585 lines
18 KiB
Diff

Index: plparse/totem-pl-parser.h
===================================================================
--- plparse/totem-pl-parser.h (revision 255)
+++ plparse/totem-pl-parser.h (working copy)
@@ -248,6 +248,8 @@
* which can be overridden by inheriting classes
* @playlist_ended: the generic signal handler for the #TotemPlParser::playlist-ended signal,
* which can be overridden by inheriting classes
+ * @parsing_finished: the generic signal handler for the #TotemPlParser::parsing-finished signal,
+ * which can be overridden by inheriting classes
*
* The class structure for the #TotemPlParser type.
**/
@@ -263,6 +265,8 @@
GHashTable *metadata);
void (*playlist_ended) (TotemPlParser *parser,
const char *uri);
+ void (*parsing_finished) (TotemPlParser *parser,
+ TotemPlParserResult retval);
};
/**
@@ -349,10 +353,16 @@
TotemPlParserResult totem_pl_parser_parse (TotemPlParser *parser,
const char *url, gboolean fallback);
+TotemPlParserResult totem_pl_parser_parse_async (TotemPlParser *parser,
+ const char *url, gboolean fallback);
TotemPlParserResult totem_pl_parser_parse_with_base (TotemPlParser *parser,
const char *url,
const char *base,
gboolean fallback);
+TotemPlParserResult totem_pl_parser_parse_with_base_async (TotemPlParser *parser,
+ const char *url,
+ const char *base,
+ gboolean fallback);
TotemPlParser *totem_pl_parser_new (void);
Index: plparse/totem-pl-parser-private.h
===================================================================
--- plparse/totem-pl-parser-private.h (revision 255)
+++ plparse/totem-pl-parser-private.h (working copy)
@@ -76,7 +76,12 @@
{
GList *ignore_schemes;
GList *ignore_mimetypes;
+ GMutex *ignore_mutex;
+ GThread *thread;
+ GAsyncQueue *parse_queue;
+ gboolean thread_stopping;
+
guint recurse_level;
guint fallback : 1;
guint recurse : 1;
Index: plparse/plparser.symbols
===================================================================
--- plparse/plparser.symbols (revision 255)
+++ plparse/plparser.symbols (working copy)
@@ -14,9 +14,11 @@
totemplparser_marshal_VOID__STRING_STRING_STRING
totem_pl_parser_new
totem_pl_parser_parse
+totem_pl_parser_parse_async
totem_pl_parser_parse_date
totem_pl_parser_parse_duration
totem_pl_parser_parse_with_base
+totem_pl_parser_parse_with_base_async
totem_pl_parser_relative
totem_pl_parser_result_get_type
totem_pl_parser_type_get_type
Index: plparse/totem-pl-parser.c
===================================================================
--- plparse/totem-pl-parser.c (revision 255)
+++ plparse/totem-pl-parser.c (working copy)
@@ -31,6 +31,9 @@
*
* <example>
* <title>Reading a Playlist</title>
+ * <para>This example loads a playlist synchronously. To load a playlist asynchronously, however, you simply
+ * need to use totem_pl_parser_parse_async(), and optionally connect to #TotemPlParser::parsing-finished
+ * to receive the ultimate #TotemPlParserResult value for the operation.</para>
* <programlisting>
* TotemPlParser *pl = totem_pl_parser_new ();
* g_object_set (pl, "recurse", FALSE, "disable-unsafe", TRUE, NULL);
@@ -234,6 +237,7 @@
ENTRY_PARSED,
PLAYLIST_STARTED,
PLAYLIST_ENDED,
+ PARSING_FINISHED,
LAST_SIGNAL
};
@@ -244,12 +248,15 @@
static void totem_pl_parser_class_init (TotemPlParserClass *klass);
static void totem_pl_parser_base_class_finalize (TotemPlParserClass *klass);
static void totem_pl_parser_init (TotemPlParser *parser);
+static void totem_pl_parser_dispose (GObject *object);
static void totem_pl_parser_finalize (GObject *object);
static void totem_pl_parser_init (TotemPlParser *self);
static void totem_pl_parser_class_init (TotemPlParserClass *klass);
static gpointer totem_pl_parser_parent_class = NULL;
+#define TOTEM_PL_PARSER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TOTEM_TYPE_PL_PARSER, TotemPlParserPrivate))
+
GType
totem_pl_parser_get_type (void)
{
@@ -279,9 +286,13 @@
GParamSpec *pspec;
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ if (!g_thread_supported ())
+ g_thread_init (NULL);
+
totem_pl_parser_parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (klass, sizeof (TotemPlParserPrivate));
+ object_class->dispose = totem_pl_parser_dispose;
object_class->finalize = totem_pl_parser_finalize;
object_class->set_property = totem_pl_parser_set_property;
object_class->get_property = totem_pl_parser_get_property;
@@ -360,6 +371,7 @@
NULL, NULL,
totemplparser_marshal_VOID__STRING_BOXED,
G_TYPE_NONE, 2, G_TYPE_STRING, TOTEM_TYPE_PL_PARSER_METADATA);
+
/**
* TotemPlParser::playlist-started:
* @parser: the object which received the signal
@@ -398,6 +410,24 @@
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
+ /**
+ * TotemPlParser::parsing-finished:
+ * @parser: the object which received the signal
+ * @retval: the #TotemPlParserResult return value from the parse operation
+ *
+ * The ::parsing-finished signal is emitted after the parse operation initiated
+ * by totem_pl_parser_parse() or totem_pl_parser_parse_with_base() has finished
+ * (successfully or not).
+ */
+ totem_pl_parser_table_signals[PARSING_FINISHED] =
+ g_signal_new ("parsing-finished",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlParserClass, parsing_finished),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__ENUM,
+ G_TYPE_NONE, 1, TOTEM_TYPE_PL_PARSER_RESULT);
+
/* param specs */
totem_pl_parser_pspec_pool = g_param_spec_pool_new (FALSE);
pspec = g_param_spec_string ("url", "url",
@@ -620,6 +650,26 @@
return TOTEM_PL_PARSER (g_object_new (TOTEM_TYPE_PL_PARSER, NULL));
}
+typedef struct {
+ TotemPlParser *parser;
+ char *playlist_uri;
+} PlaylistEndedSignalData;
+
+static gboolean
+emit_playlist_ended_signal (PlaylistEndedSignalData *data)
+{
+ g_signal_emit (data->parser,
+ totem_pl_parser_table_signals[PLAYLIST_ENDED],
+ 0, data->playlist_uri);
+
+ /* Free the data */
+ g_object_unref (data->parser);
+ g_free (data->playlist_uri);
+ g_free (data);
+
+ return FALSE;
+}
+
/**
* totem_pl_parser_playlist_end:
* @parser: a #TotemPlParser
@@ -631,9 +681,13 @@
void
totem_pl_parser_playlist_end (TotemPlParser *parser, const char *playlist_uri)
{
- g_signal_emit (G_OBJECT (parser),
- totem_pl_parser_table_signals[PLAYLIST_ENDED],
- 0, playlist_uri);
+ PlaylistEndedSignalData *data;
+
+ data = g_new (PlaylistEndedSignalData, 1);
+ data->parser = g_object_ref (parser);
+ data->playlist_uri = g_strdup (playlist_uri);
+
+ g_idle_add ((GSourceFunc) emit_playlist_ended_signal, data);
}
static char *
@@ -1057,25 +1111,70 @@
totem_pl_parser_init (TotemPlParser *parser)
{
parser->priv = G_TYPE_INSTANCE_GET_PRIVATE (parser, TOTEM_TYPE_PL_PARSER, TotemPlParserPrivate);
+ parser->priv->ignore_mutex = g_mutex_new ();
}
static void
+totem_pl_parser_dispose (GObject *object)
+{
+ TotemPlParserPrivate *priv = TOTEM_PL_PARSER_GET_PRIVATE (object);
+
+ /* Tell the thread we're stopping */
+ priv->thread_stopping = TRUE;
+
+ /* Wait for the thread to finish */
+ if (priv->thread != NULL)
+ g_thread_join (priv->thread);
+ priv->thread = NULL;
+
+ /* Free the job queue */
+ if (priv->parse_queue != NULL)
+ g_async_queue_unref (priv->parse_queue);
+ priv->parse_queue = NULL;
+
+ G_OBJECT_CLASS (totem_pl_parser_parent_class)->dispose (object);
+}
+
+static void
totem_pl_parser_finalize (GObject *object)
{
- TotemPlParser *parser = TOTEM_PL_PARSER (object);
+ TotemPlParserPrivate *priv = TOTEM_PL_PARSER_GET_PRIVATE (object);
g_return_if_fail (object != NULL);
- g_return_if_fail (parser->priv != NULL);
+ g_return_if_fail (priv != NULL);
- g_list_foreach (parser->priv->ignore_schemes, (GFunc) g_free, NULL);
- g_list_free (parser->priv->ignore_schemes);
+ g_list_foreach (priv->ignore_schemes, (GFunc) g_free, NULL);
+ g_list_free (priv->ignore_schemes);
- g_list_foreach (parser->priv->ignore_mimetypes, (GFunc) g_free, NULL);
- g_list_free (parser->priv->ignore_mimetypes);
+ g_list_foreach (priv->ignore_mimetypes, (GFunc) g_free, NULL);
+ g_list_free (priv->ignore_mimetypes);
+ g_mutex_free (priv->ignore_mutex);
+
G_OBJECT_CLASS (totem_pl_parser_parent_class)->finalize (object);
}
+typedef struct {
+ TotemPlParser *parser;
+ guint signal_id;
+ char *url;
+ GHashTable *metadata;
+} EntryParsedSignalData;
+
+static gboolean
+emit_entry_parsed_signal (EntryParsedSignalData *data)
+{
+ g_signal_emit (data->parser, data->signal_id, 0, data->url, data->metadata);
+
+ /* Free the data */
+ g_object_unref (data->parser);
+ g_free (data->url);
+ g_hash_table_unref (data->metadata);
+ g_free (data);
+
+ return FALSE;
+}
+
static void
totem_pl_parser_add_url_valist (TotemPlParser *parser,
const gchar *first_property_name,
@@ -1188,15 +1287,21 @@
}
if (g_hash_table_size (metadata) > 0 || url != NULL) {
- if (is_playlist == FALSE) {
- g_signal_emit (G_OBJECT (parser),
- totem_pl_parser_table_signals[ENTRY_PARSED],
- 0, url, metadata);
- } else {
- g_signal_emit (G_OBJECT (parser),
- totem_pl_parser_table_signals[PLAYLIST_STARTED],
- 0, url, metadata);
- }
+ EntryParsedSignalData *data;
+
+ /* Make sure to emit the signals asynchronously, as we could be in the main loop
+ * *or* a worker thread at this point. */
+ data = g_new (EntryParsedSignalData, 1);
+ data->parser = g_object_ref (parser);
+ data->url = g_strdup (url);
+ data->metadata = g_hash_table_ref (metadata);
+
+ if (is_playlist == FALSE)
+ data->signal_id = totem_pl_parser_table_signals[ENTRY_PARSED];
+ else
+ data->signal_id = totem_pl_parser_table_signals[PLAYLIST_STARTED];
+
+ g_idle_add ((GSourceFunc) emit_entry_parsed_signal, data);
}
g_hash_table_unref (metadata);
@@ -1275,15 +1380,23 @@
{
GList *l;
- if (parser->priv->ignore_schemes == NULL)
+ g_mutex_lock (parser->priv->ignore_mutex);
+
+ if (parser->priv->ignore_schemes == NULL) {
+ g_mutex_unlock (parser->priv->ignore_mutex);
return FALSE;
+ }
for (l = parser->priv->ignore_schemes; l != NULL; l = l->next) {
const char *scheme = l->data;
- if (g_file_has_uri_scheme (file, scheme) != FALSE)
+ if (g_file_has_uri_scheme (file, scheme) != FALSE) {
+ g_mutex_unlock (parser->priv->ignore_mutex);
return TRUE;
+ }
}
+ g_mutex_unlock (parser->priv->ignore_mutex);
+
return FALSE;
}
@@ -1293,16 +1406,24 @@
{
GList *l;
- if (parser->priv->ignore_mimetypes == NULL)
+ g_mutex_lock (parser->priv->ignore_mutex);
+
+ if (parser->priv->ignore_mimetypes == NULL) {
+ g_mutex_unlock (parser->priv->ignore_mutex);
return FALSE;
+ }
for (l = parser->priv->ignore_mimetypes; l != NULL; l = l->next)
{
const char *item = l->data;
- if (strcmp (mimetype, item) == 0)
+ if (strcmp (mimetype, item) == 0) {
+ g_mutex_unlock (parser->priv->ignore_mutex);
return TRUE;
+ }
}
+ g_mutex_unlock (parser->priv->ignore_mutex);
+
return FALSE;
}
@@ -1596,6 +1717,70 @@
return ret;
}
+typedef struct {
+ char *url;
+ char *base;
+ gboolean fallback;
+ TotemPlParserResult retval;
+ TotemPlParser *parser;
+} ParseQueueItem;
+
+static void
+parse_queue_item_free (ParseQueueItem *item)
+{
+ g_free (item->url);
+ g_free (item->base);
+ g_object_unref (item->parser);
+
+ g_free (item);
+}
+
+static gboolean
+totem_pl_parser_thread_job_finished (ParseQueueItem *queue_item)
+{
+ g_signal_emit (queue_item->parser, totem_pl_parser_table_signals[PARSING_FINISHED], 0, queue_item->retval);
+ parse_queue_item_free (queue_item);
+ return FALSE;
+}
+
+static gpointer
+totem_pl_parser_thread_main (TotemPlParser *parser)
+{
+ while (parser->priv->thread_stopping == FALSE) {
+ GFile *file, *base_file;
+ TotemPlParserResult retval;
+ ParseQueueItem *queue_item;
+
+ /* Get a job off the queue */
+ queue_item = g_async_queue_pop (parser->priv->parse_queue);
+
+ /* Start parsing it */
+ file = g_file_new_for_uri (queue_item->url);
+ base_file = NULL;
+
+ if (totem_pl_parser_scheme_is_ignored (parser, file) != FALSE) {
+ g_object_unref (file);
+ retval = TOTEM_PL_PARSER_RESULT_UNHANDLED;
+ } else {
+ parser->priv->recurse_level = 0;
+ parser->priv->fallback = queue_item->fallback != FALSE;
+ if (queue_item->base != NULL)
+ base_file = g_file_new_for_uri (queue_item->base);
+ retval = totem_pl_parser_parse_internal (parser, file, base_file);
+
+ g_object_unref (file);
+ if (base_file != NULL)
+ g_object_unref (base_file);
+ }
+
+ /* Emit the "parsing-finished" signal for this job from the main thread */
+ queue_item->retval = retval;
+ g_idle_add ((GSourceFunc) totem_pl_parser_thread_job_finished, queue_item);
+ }
+
+ return NULL;
+}
+
/**
* totem_pl_parser_parse_with_base:
* @parser: a #TotemPlParser
@@ -1605,8 +1790,14 @@
* end of the playlist on parse failure
*
* Parses a playlist given by the absolute URL @url, using
- * @base to resolve relative paths where appropriate.
+ * @base to resolve relative paths where appropriate. This method is
+ * synchronous, and will block on (e.g.) network requests to slow
+ * servers. totem_pl_parser_parse_with_base_async() is recommended instead.
*
+ * This function will return %TOTEM_PL_PARSER_RESULT_SUCCESS on success,
+ * and either %TOTEM_PL_PARSER_RESULT_UNHANDLED or
+ * %TOTEM_PL_PARSER_RESULT_ERROR if the parameters were invalid.
+ *
* Return value: a #TotemPlParserResult
**/
TotemPlParserResult
@@ -1618,8 +1809,7 @@
g_return_val_if_fail (TOTEM_IS_PL_PARSER (parser), TOTEM_PL_PARSER_RESULT_UNHANDLED);
g_return_val_if_fail (url != NULL, TOTEM_PL_PARSER_RESULT_UNHANDLED);
- g_return_val_if_fail (strstr (url, "://") != NULL,
- TOTEM_PL_PARSER_RESULT_ERROR);
+ g_return_val_if_fail (strstr (url, "://") != NULL, TOTEM_PL_PARSER_RESULT_ERROR);
file = g_file_new_for_uri (url);
base_file = NULL;
@@ -1643,14 +1833,77 @@
}
/**
+ * totem_pl_parser_parse_with_base_async:
+ * @parser: a #TotemPlParser
+ * @url: the URL of the playlist to parse
+ * @base: the base path for relative filenames
+ * @fallback: %TRUE if the parser should add the playlist URL to the
+ * end of the playlist on parse failure
+ *
+ * Starts asynchronous parsing of a playlist given by the absolute URL @url,
+ * using @base to resolve relative paths where appropriate.
+ *
+ * This method is asynchronous, and #TotemPlParser::parsing-finished will be
+ * emitted once parsing is finished (successfully or not).
+ *
+ * This function will return %TOTEM_PL_PARSER_RESULT_SUCCESS if the job
+ * was successfully added to the parse queue, and either
+ * %TOTEM_PL_PARSER_RESULT_UNHANDLED or %TOTEM_PL_PARSER_RESULT_ERROR
+ * if the parameters were invalid.
+ *
+ * Return value: a #TotemPlParserResult
+ **/
+TotemPlParserResult
+totem_pl_parser_parse_with_base_async (TotemPlParser *parser, const char *url,
+ const char *base, gboolean fallback)
+{
+ ParseQueueItem *queue_item;
+
+ g_return_val_if_fail (TOTEM_IS_PL_PARSER (parser), TOTEM_PL_PARSER_RESULT_UNHANDLED);
+ g_return_val_if_fail (url != NULL, TOTEM_PL_PARSER_RESULT_UNHANDLED);
+ g_return_val_if_fail (strstr (url, "://") != NULL, TOTEM_PL_PARSER_RESULT_ERROR);
+
+ if (parser->priv->thread == NULL) {
+ GError *error = NULL;
+
+ /* Create the worker thread */
+ parser->priv->parse_queue = g_async_queue_new_full ((GDestroyNotify) parse_queue_item_free);
+ parser->priv->thread = g_thread_create ((GThreadFunc) totem_pl_parser_thread_main, parser, TRUE, &error);
+
+ if (error != NULL) {
+ g_warning ("Failed to create TotemPlParser worker thread: %s", error->message);
+ g_async_queue_unref (parser->priv->parse_queue);
+ g_error_free (error);
+
+ return TOTEM_PL_PARSER_RESULT_ERROR;
+ }
+ }
+
+ /* Thread already exists, so add the parse job to its queue */
+ queue_item = g_new (ParseQueueItem, 1);
+ queue_item->url = g_strdup (url);
+ queue_item->base = g_strdup (base);
+ queue_item->fallback = fallback;
+ queue_item->parser = g_object_ref (parser);
+
+ g_async_queue_push (parser->priv->parse_queue, queue_item);
+
+ return TOTEM_PL_PARSER_RESULT_SUCCESS;
+}
+
+/**
* totem_pl_parser_parse:
* @parser: a #TotemPlParser
* @url: the URL of the playlist to parse
* @fallback: %TRUE if the parser should add the playlist URL to the
* end of the playlist on parse failure
*
- * Parses a playlist given by the absolute URL @url.
+ * Parses a playlist given by the absolute URL @url. This method is
+ * synchronous, and will block on (e.g.) network requests to slow
+ * servers. totem_pl_parser_parse_async() is recommended instead.
*
+ * Return values are as totem_pl_parser_parse_with_base().
+ *
* Return value: a #TotemPlParserResult
**/
TotemPlParserResult
@@ -1661,6 +1914,29 @@
}
/**
+ * totem_pl_parser_parse_async:
+ * @parser: a #TotemPlParser
+ * @url: the URL of the playlist to parse
+ * @fallback: %TRUE if the parser should add the playlist URL to the
+ * end of the playlist on parse failure
+ *
+ * Starts asynchronous parsing of a playlist given by the absolute URL @url.
+ *
+ * This method is asynchronous, and #TotemPlParser::parsing-finished will be
+ * emitted once parsing is finished (successfully or not).
+ *
+ * Return values are as totem_pl_parser_parse_with_base_async().
+ *
+ * Return value: a #TotemPlParserResult
+ **/
+TotemPlParserResult
+totem_pl_parser_parse_async (TotemPlParser *parser, const char *url,
+ gboolean fallback)
+{
+ return totem_pl_parser_parse_with_base_async (parser, url, NULL, fallback);
+}
+
+/**
* totem_pl_parser_add_ignored_scheme:
* @parser: a #TotemPlParser
* @scheme: the scheme to ignore
@@ -1676,11 +1952,15 @@
g_return_if_fail (TOTEM_IS_PL_PARSER (parser));
+ g_mutex_lock (parser->priv->ignore_mutex);
+
s = g_strdup (scheme);
if (s[strlen (s) - 1] == ':')
s[strlen (s) - 1] = '\0';
parser->priv->ignore_schemes = g_list_prepend
(parser->priv->ignore_schemes, s);
+
+ g_mutex_unlock (parser->priv->ignore_mutex);
}
/**