[rhythmbox] lib: rewrite rb_uri_handle_recursively_async
- From: Jonathan Matthew <jmatthew src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rhythmbox] lib: rewrite rb_uri_handle_recursively_async
- Date: Sat, 9 Nov 2013 10:23:33 +0000 (UTC)
commit 16e585887b754e3a3e23e289c4f0b1c6b91c29ed
Author: Jonathan Matthew <jonathan d14n org>
Date: Sat Nov 9 20:17:54 2013 +1000
lib: rewrite rb_uri_handle_recursively_async
The new implementation uses async gio methods rather than creating
a thread and calling rb_uri_handle_recursively. This avoids some
hassles with main loop contexts and should also be a bit faster.
lib/rb-file-helpers.c | 323 ++++++++++++++++++++++------------------
rhythmdb/rhythmdb-import-job.c | 7 +-
2 files changed, 184 insertions(+), 146 deletions(-)
---
diff --git a/lib/rb-file-helpers.c b/lib/rb-file-helpers.c
index f7924f1..156efa7 100644
--- a/lib/rb-file-helpers.c
+++ b/lib/rb-file-helpers.c
@@ -51,6 +51,12 @@
#include "rb-debug.h"
#include "rb-util.h"
+typedef struct _RBUriHandleRecursivelyAsyncData RBUriHandleRecursivelyAsyncData;
+
+static void _uri_handle_recursively_free (RBUriHandleRecursivelyAsyncData *data);
+static void _uri_handle_recursively_next_dir (RBUriHandleRecursivelyAsyncData *data);
+static void _uri_handle_recursively_next_files (RBUriHandleRecursivelyAsyncData *data);
+
static GHashTable *files = NULL;
static char *dot_dir = NULL;
@@ -77,6 +83,13 @@ static char *installed_paths[] = {
static char **search_paths;
+static const char *recurse_attributes =
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_ID_FILE ","
+ G_FILE_ATTRIBUTE_ACCESS_CAN_READ;
+
/**
* rb_locale_dir:
*
@@ -672,19 +685,6 @@ rb_uri_make_hidden (const char *uri)
return ret;
}
-typedef struct {
- char *uri;
- GCancellable *cancel;
- RBUriRecurseFunc func;
- gpointer user_data;
- GDestroyNotify data_destroy;
-
- GMutex results_lock;
- guint results_idle_id;
- GList *file_results;
- GList *dir_results;
-} RBUriHandleRecursivelyAsyncData;
-
static gboolean
_should_process (GFileInfo *info)
{
@@ -702,6 +702,54 @@ _should_process (GFileInfo *info)
return TRUE;
}
+
+static gboolean
+_uri_handle_file (GFile *dir, GFileInfo *fileinfo, GHashTable *handled, RBUriRecurseFunc func, gpointer
user_data, GFile **descend)
+{
+ const char *file_id;
+ gboolean is_dir;
+ gboolean ret;
+ GFileType file_type;
+ GFile *child;
+
+ *descend = NULL;
+ if (_should_process (fileinfo) == FALSE) {
+ rb_debug ("ignoring %s", g_file_info_get_name (fileinfo));
+ return TRUE;
+ }
+
+ /* already handled? */
+ file_id = g_file_info_get_attribute_string (fileinfo, G_FILE_ATTRIBUTE_ID_FILE);
+ if (file_id == NULL) {
+ /* have to hope for the best, I guess */
+ } else if (g_hash_table_lookup (handled, file_id) != NULL) {
+ return TRUE;
+ } else {
+ g_hash_table_insert (handled, g_strdup (file_id), GINT_TO_POINTER (1));
+ }
+
+ /* type? */
+ file_type = g_file_info_get_attribute_uint32 (fileinfo, G_FILE_ATTRIBUTE_STANDARD_TYPE);
+ switch (file_type) {
+ case G_FILE_TYPE_DIRECTORY:
+ case G_FILE_TYPE_MOUNTABLE:
+ is_dir = TRUE;
+ break;
+ default:
+ is_dir = FALSE;
+ break;
+ }
+
+ child = g_file_get_child (dir, g_file_info_get_name (fileinfo));
+ ret = (func) (child, is_dir, user_data);
+ if (is_dir && ret) {
+ *descend = child;
+ } else {
+ g_object_unref (child);
+ }
+ return ret;
+}
+
static void
_uri_handle_recurse (GFile *dir,
GCancellable *cancel,
@@ -712,24 +760,16 @@ _uri_handle_recurse (GFile *dir,
GFileEnumerator *files;
GFileInfo *info;
GError *error = NULL;
- GFileType file_type;
- const char *file_id;
- gboolean file_handled;
- const char *attributes =
- G_FILE_ATTRIBUTE_STANDARD_NAME ","
- G_FILE_ATTRIBUTE_STANDARD_TYPE ","
- G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
- G_FILE_ATTRIBUTE_ID_FILE ","
- G_FILE_ATTRIBUTE_ACCESS_CAN_READ;
+ GFile *descend;
- files = g_file_enumerate_children (dir, attributes, G_FILE_QUERY_INFO_NONE, cancel, &error);
+ files = g_file_enumerate_children (dir, recurse_attributes, G_FILE_QUERY_INFO_NONE, cancel, &error);
if (error != NULL) {
char *where;
/* handle the case where we're given a single file to process */
if (error->code == G_IO_ERROR_NOT_DIRECTORY) {
g_clear_error (&error);
- info = g_file_query_info (dir, attributes, G_FILE_QUERY_INFO_NONE, cancel, &error);
+ info = g_file_query_info (dir, recurse_attributes, G_FILE_QUERY_INFO_NONE, cancel,
&error);
if (error == NULL) {
if (_should_process (info)) {
(func) (dir, FALSE, user_data);
@@ -747,11 +787,6 @@ _uri_handle_recurse (GFile *dir,
}
while (1) {
- GFile *child;
- gboolean is_dir;
- gboolean ret;
-
- ret = TRUE;
info = g_file_enumerator_next_file (files, cancel, &error);
if (error != NULL) {
rb_debug ("error enumerating files: %s", error->message);
@@ -760,50 +795,13 @@ _uri_handle_recurse (GFile *dir,
break;
}
- if (_should_process (info) == FALSE) {
- g_object_unref (info);
- continue;
- }
-
- /* already handled? */
- file_id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE);
- if (file_id == NULL) {
- /* have to hope for the best, I guess */
- file_handled = FALSE;
- } else if (g_hash_table_lookup (handled, file_id) != NULL) {
- file_handled = TRUE;
- } else {
- file_handled = FALSE;
- g_hash_table_insert (handled, g_strdup (file_id), GINT_TO_POINTER (1));
- }
-
- /* type? */
- file_type = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE);
- switch (file_type) {
- case G_FILE_TYPE_DIRECTORY:
- case G_FILE_TYPE_MOUNTABLE:
- is_dir = TRUE;
- break;
-
- default:
- is_dir = FALSE;
+ if (_uri_handle_file (dir, info, handled, func, user_data, &descend) == FALSE)
break;
- }
- if (file_handled == FALSE) {
- child = g_file_get_child (dir, g_file_info_get_name (info));
- ret = (func) (child, is_dir, user_data);
-
- if (is_dir) {
- _uri_handle_recurse (child, cancel, handled, func, user_data);
- }
- g_object_unref (child);
+ if (descend) {
+ _uri_handle_recurse (descend, cancel, handled, func, user_data);
+ g_object_unref (descend);
}
-
- g_object_unref (info);
-
- if (ret == FALSE)
- break;
}
g_object_unref (files);
@@ -837,95 +835,134 @@ rb_uri_handle_recursively (const char *uri,
g_object_unref (file);
}
+struct _RBUriHandleRecursivelyAsyncData {
+ GCancellable *cancel;
+ RBUriRecurseFunc func;
+ gpointer user_data;
+ GDestroyNotify data_destroy;
-/* runs in main thread */
-static gboolean
-_recurse_async_idle_cb (RBUriHandleRecursivelyAsyncData *data)
-{
- GList *ul, *dl;
-
- g_mutex_lock (&data->results_lock);
-
- for (ul = data->file_results, dl = data->dir_results;
- ul != NULL;
- ul = g_list_next (ul), dl = g_list_next (dl)) {
- g_assert (dl != NULL);
+ GHashTable *handled;
- data->func (G_FILE (ul->data), (GPOINTER_TO_INT (dl->data) == 1), data->user_data);
- g_object_unref (ul->data);
- }
- g_assert (dl == NULL);
+ GQueue *dirs_left;
+ GFile *current;
+ GFileEnumerator *enumerator;
+};
- g_list_free (data->file_results);
- data->file_results = NULL;
- g_list_free (data->dir_results);
- data->dir_results = NULL;
- data->results_idle_id = 0;
- g_mutex_unlock (&data->results_lock);
- return FALSE;
+static void
+_uri_handle_recursively_free (RBUriHandleRecursivelyAsyncData *data)
+{
+ if (data->data_destroy)
+ data->data_destroy (data->user_data);
+ g_clear_object (&data->current);
+ g_clear_object (&data->enumerator);
+ g_clear_object (&data->cancel);
+ g_hash_table_destroy (data->handled);
+ g_queue_free_full (data->dirs_left, g_object_unref);
+ g_free (data);
}
-/* runs in main thread */
-static gboolean
-_recurse_async_data_free (RBUriHandleRecursivelyAsyncData *data)
+static void
+_uri_handle_recursively_process_files (GObject *src, GAsyncResult *result, gpointer ptr)
{
- GList *i;
+ GList *files;
+ GList *l;
+ GFile *descend;
+ GError *error = NULL;
+ RBUriHandleRecursivelyAsyncData *data = ptr;
- if (data->results_idle_id) {
- g_source_remove (data->results_idle_id);
- _recurse_async_idle_cb (data); /* process last results */
+ files = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (src), result, &error);
+ if (error != NULL) {
+ rb_debug ("error enumerating files: %s", error->message);
+ _uri_handle_recursively_next_dir (data);
+ g_clear_error (&error);
+ return;
}
- for (i = data->file_results; i != NULL; i = i->next) {
- GFile *file = G_FILE (i->data);
- g_object_unref (file);
+ if (files == NULL) {
+ _uri_handle_recursively_next_dir (data);
+ return;
}
- g_list_free (data->file_results);
- data->file_results = NULL;
- g_list_free (data->dir_results);
- data->dir_results = NULL;
-
- if (data->data_destroy != NULL) {
- (data->data_destroy) (data->user_data);
- }
- if (data->cancel != NULL) {
- g_object_unref (data->cancel);
+ rb_debug ("got %d file(s)", g_list_length (files));
+ for (l = files; l != NULL; l = l->next) {
+ descend = NULL;
+ if (_uri_handle_file (data->current, l->data, data->handled, data->func, data->user_data,
&descend) == FALSE) {
+ rb_debug ("callback returned false");
+ g_cancellable_cancel (data->cancel);
+ break;
+ } else if (descend) {
+ char *uri = g_file_get_uri (descend);
+ rb_debug ("adding dir %s to processing list", uri);
+ g_free (uri);
+ g_queue_push_tail (data->dirs_left, descend);
+ }
}
- g_free (data->uri);
- return FALSE;
+ g_list_free_full (files, g_object_unref);
+ _uri_handle_recursively_next_files (data);
}
-/* runs in worker thread */
-static gboolean
-_recurse_async_cb (GFile *file, gboolean dir, RBUriHandleRecursivelyAsyncData *data)
+static void
+_uri_handle_recursively_next_files (RBUriHandleRecursivelyAsyncData *data)
{
- g_mutex_lock (&data->results_lock);
+ g_file_enumerator_next_files_async (data->enumerator,
+ 16, /* or something */
+ G_PRIORITY_DEFAULT,
+ data->cancel,
+ _uri_handle_recursively_process_files,
+ data);
+}
- data->file_results = g_list_prepend (data->file_results, g_object_ref (file));
- data->dir_results = g_list_prepend (data->dir_results, GINT_TO_POINTER (dir ? 1 : 0));
- if (data->results_idle_id == 0) {
- g_idle_add ((GSourceFunc)_recurse_async_idle_cb, data);
- }
+static void
+_uri_handle_recursively_enum_files (GObject *src, GAsyncResult *result, gpointer ptr)
+{
+ GError *error = NULL;
+ RBUriHandleRecursivelyAsyncData *data = ptr;
- g_mutex_unlock (&data->results_lock);
- return TRUE;
+ data->enumerator = g_file_enumerate_children_finish (G_FILE (src), result, &error);
+ if (error != NULL) {
+ if (error->code == G_IO_ERROR_NOT_DIRECTORY) {
+ GFileInfo *info;
+
+ info = g_file_query_info (G_FILE (src), recurse_attributes, G_FILE_QUERY_INFO_NONE,
data->cancel, &error);
+ if (error == NULL) {
+ if (_should_process (info)) {
+ (data->func) (G_FILE (src), FALSE, data->user_data);
+ }
+ g_object_unref (info);
+ }
+ } else {
+ rb_debug ("error enumerating folder: %s", error->message);
+ }
+ g_clear_error (&error);
+ _uri_handle_recursively_next_dir (data);
+ } else {
+ _uri_handle_recursively_next_files (data);
+ }
}
-static gpointer
-_recurse_async_func (RBUriHandleRecursivelyAsyncData *data)
+static void
+_uri_handle_recursively_next_dir (RBUriHandleRecursivelyAsyncData *data)
{
- rb_uri_handle_recursively (data->uri,
- data->cancel,
- (RBUriRecurseFunc) _recurse_async_cb,
- data);
-
- g_idle_add ((GSourceFunc)_recurse_async_data_free, data);
- return NULL;
+ g_clear_object (&data->current);
+ g_clear_object (&data->enumerator);
+ data->current = g_queue_pop_head (data->dirs_left);
+ if (data->current != NULL) {
+ g_file_enumerate_children_async (data->current,
+ recurse_attributes,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ data->cancel,
+ _uri_handle_recursively_enum_files,
+ data);
+ } else {
+ rb_debug ("nothing more to do");
+ _uri_handle_recursively_free (data);
+ }
}
+
/**
* rb_uri_handle_recursively_async:
* @uri: the URI to visit
@@ -938,9 +975,6 @@ _recurse_async_func (RBUriHandleRecursivelyAsyncData *data)
* by @uri, or if @uri identifies a file, calls it once
* with that.
*
- * Directory recursion happens on a separate thread, but the callbacks
- * are called on the main thread.
- *
* If non-NULL, @destroy_data will be called once all files have been
* processed, or when the operation is cancelled.
*/
@@ -953,18 +987,21 @@ rb_uri_handle_recursively_async (const char *uri,
{
RBUriHandleRecursivelyAsyncData *data = g_new0 (RBUriHandleRecursivelyAsyncData, 1);
- data->uri = g_strdup (uri);
- data->user_data = user_data;
+ rb_debug ("processing %s", uri);
if (cancel != NULL) {
data->cancel = g_object_ref (cancel);
+ } else {
+ data->cancel = g_cancellable_new ();
}
- data->data_destroy = data_destroy;
- g_mutex_init (&data->results_lock);
data->func = func;
data->user_data = user_data;
+ data->data_destroy = data_destroy;
+ data->handled = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
- g_thread_new ("rb-uri-recurse", (GThreadFunc)_recurse_async_func, data);
+ data->dirs_left = g_queue_new ();
+ g_queue_push_tail (data->dirs_left, g_file_new_for_uri (uri));
+ _uri_handle_recursively_next_dir (data);
}
/**
diff --git a/rhythmdb/rhythmdb-import-job.c b/rhythmdb/rhythmdb-import-job.c
index 146a46b..7193e86 100644
--- a/rhythmdb/rhythmdb-import-job.c
+++ b/rhythmdb/rhythmdb-import-job.c
@@ -313,18 +313,18 @@ emit_status_changed (RhythmDBImportJob *job)
return FALSE;
}
-static void
+static gboolean
uri_recurse_func (GFile *file, gboolean dir, RhythmDBImportJob *job)
{
RhythmDBEntry *entry;
char *uri;
if (dir) {
- return;
+ return TRUE;
}
if (g_cancellable_is_cancelled (job->priv->cancel))
- return;
+ return FALSE;
uri = g_file_get_uri (file);
@@ -359,6 +359,7 @@ uri_recurse_func (GFile *file, gboolean dir, RhythmDBImportJob *job)
}
g_free (uri);
+ return TRUE;
}
static gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]