The attached patch tries to fix moving/copying of metadata if the source or destination metafile are not yet read. I'd like to get some feedback whether the overall architecture/resolution proposal looks sane. Maybe people familiar with async APIs do see some shortcomings/issues? One question remains, though: What should we do if the metadata reading fails? For now, the copy process stays in pending_copies, possibly forever. -- Christian Neumair <chris gnome-de org>
Index: libnautilus-private/nautilus-metafile.c =================================================================== RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-metafile.c,v retrieving revision 1.42 diff -u -p -r1.42 nautilus-metafile.c --- libnautilus-private/nautilus-metafile.c 19 Sep 2005 13:51:16 -0000 1.42 +++ libnautilus-private/nautilus-metafile.c 19 Sep 2005 14:35:29 -0000 @@ -74,8 +74,14 @@ static void copy_file_metadata const char *source_file_name, NautilusMetafile *destination_metafile, const char *destination_file_name); +static void real_copy_file_metadata (NautilusMetafile *source_metafile, + const char *source_file_name, + NautilusMetafile *destination_metafile, + const char *destination_file_name); static void remove_file_metadata (NautilusMetafile *metafile, const char *file_name); +static void real_remove_file_metadata (NautilusMetafile *metafile, + const char *file_name); static void call_metafile_changed_for_one_file (NautilusMetafile *metafile, const CORBA_char *file_name); static void metafile_read_restart (NautilusMetafile *metafile); @@ -259,6 +265,206 @@ nautilus_metafile_get (const char *direc return metafile; } +static GList *pending_copies; + +typedef struct { + NautilusMetafile *source_metafile; + char *source_file_name; + NautilusMetafile *destination_metafile; + char *destination_file_name; +} NautilusMetadataCopy; + +static gboolean +nautilus_metadata_copy_equal (const NautilusMetadataCopy *a, + const NautilusMetadataCopy *b) +{ + return (b->source_metafile == a->source_metafile) + && (b->destination_metafile == a->destination_metafile) + && (strcmp (a->source_file_name, b->source_file_name) == 0) + && (strcmp (a->destination_file_name, b->destination_file_name) == 0); +} + +static NautilusMetadataCopy * +nautilus_metadata_get_scheduled_copy (NautilusMetafile *source_metafile, + const char *source_file_name, + NautilusMetafile *destination_metafile, + const char *destination_file_name) +{ + NautilusMetadataCopy key, *copy; + GList *l; + + key.source_metafile = source_metafile; + key.source_file_name = (char *) source_file_name; + key.destination_metafile = destination_metafile; + key.destination_file_name = (char *) destination_file_name; + + for (l = pending_copies; l != NULL; l = l->next) { + copy = l->data; + + if (nautilus_metadata_copy_equal (l->data, &key)) { + return copy; + } + } + + return NULL; +} + +static gboolean +nautilus_metadata_has_scheduled_copy (NautilusMetafile *source_metafile, + const char *source_file_name) +{ + NautilusMetadataCopy *copy; + GList *l; + + for (l = pending_copies; l != NULL; l = l->next) { + copy = l->data; + + if ((copy->source_metafile == source_metafile) + && (strcmp (copy->source_file_name, source_file_name) == 0)) { + return TRUE; + } + } + + return FALSE; +} + +static void +nautilus_metadata_schedule_copy (NautilusMetafile *source_metafile, + const char *source_file_name, + NautilusMetafile *destination_metafile, + const char *destination_file_name) +{ + NautilusMetadataCopy *copy; + + g_assert (!source_metafile->details->is_read || !destination_metafile->details->is_read); + + copy = nautilus_metadata_get_scheduled_copy (source_metafile, + source_file_name, + destination_metafile, + destination_file_name); + if (copy == NULL) { + copy = g_malloc (sizeof (NautilusMetadataCopy)); + copy->source_metafile = bonobo_object_ref (source_metafile); + copy->source_file_name = g_strdup (source_file_name); + copy->destination_metafile = bonobo_object_ref (destination_metafile); + copy->destination_file_name = g_strdup (destination_file_name); + + pending_copies = g_list_prepend (pending_copies, copy); + + metafile_read_start (source_metafile); + metafile_read_start (destination_metafile); + } +} + +static void +nautilus_metadata_process_ready_copies (void) +{ + NautilusMetadataCopy *copy; + GList *l, *next; + + l = pending_copies; + while (l != NULL) { + copy = l->data; + + next = l->next; + + if (copy->source_metafile->details->is_read + && copy->destination_metafile->details->is_read) { + real_copy_file_metadata (copy->source_metafile, copy->source_file_name, + copy->destination_metafile, copy->destination_file_name); + + bonobo_object_unref (copy->source_metafile); + g_free (copy->source_file_name); + bonobo_object_unref (copy->destination_metafile); + g_free (copy->destination_file_name); + g_free (copy); + + pending_copies = g_list_delete_link (pending_copies, l); + } + + l = next; + } +} + +static GList *pending_removals; + +typedef struct { + NautilusMetafile *metafile; + char *file_name; +} NautilusMetadataRemoval; + +static gboolean +nautilus_metadata_removal_equal (const NautilusMetadataRemoval *a, + const NautilusMetadataRemoval *b) +{ + return ((b->metafile == a->metafile) + && (strcmp (a->file_name, b->file_name) == 0)); +} + +static NautilusMetadataRemoval * +nautilus_metadata_get_scheduled_removal (NautilusMetafile *metafile, + const char *file_name) +{ + NautilusMetadataRemoval key, *removal; + GList *l; + + key.metafile = metafile; + key.file_name = (char *) file_name; + + for (l = pending_removals; l != NULL; l = l->next) { + removal = l->data; + + if (nautilus_metadata_removal_equal (l->data, &key)) { + return removal; + } + } + + return NULL; +} + +static void +nautilus_metadata_schedule_removal (NautilusMetafile *metafile, + const char *file_name) +{ + NautilusMetadataRemoval *removal; + + g_assert (nautilus_metadata_has_scheduled_copy (metafile, file_name)); + + removal = nautilus_metadata_get_scheduled_removal (metafile, file_name); + if (removal == NULL) { + removal = g_malloc (sizeof (NautilusMetadataRemoval)); + removal->metafile = bonobo_object_ref (metafile); + removal->file_name = g_strdup (file_name); + + pending_removals = g_list_prepend (pending_removals, removal); + } +} + +static void +nautilus_metadata_process_ready_removals (void) +{ + NautilusMetadataRemoval *removal; + GList *l, *next; + + l = pending_removals; + while (l != NULL) { + removal = l->data; + + next = l->next; + + if (!nautilus_metadata_has_scheduled_copy (removal->metafile, removal->file_name)) { + real_remove_file_metadata (removal->metafile, removal->file_name); + + pending_removals = g_list_delete_link (pending_removals, l); + + bonobo_object_unref (removal->metafile); + g_free (removal->file_name); + } + + l = next; + } +} + /* FIXME * Right now we only limit the number of conccurrent reads. * We may want to consider limiting writes as well. @@ -1407,46 +1613,27 @@ nautilus_metafile_apply_pending_changes } static void -copy_file_metadata (NautilusMetafile *source_metafile, - const char *source_file_name, - NautilusMetafile *destination_metafile, - const char *destination_file_name) +real_copy_file_metadata (NautilusMetafile *source_metafile, + const char *source_file_name, + NautilusMetafile *destination_metafile, + const char *destination_file_name) { xmlNodePtr source_node, node, root; GHashTable *hash, *changes; - char *source_file_uri; - char *destination_file_uri; - g_return_if_fail (NAUTILUS_IS_METAFILE (source_metafile)); - g_return_if_fail (source_file_name != NULL); - g_return_if_fail (NAUTILUS_IS_METAFILE (destination_metafile)); - g_return_if_fail (destination_file_name != NULL); - - /* FIXME bugzilla.gnome.org 43343: This does not properly - * handle the case where we don't have the source metadata yet - * since it's not read in. - */ - - remove_file_metadata - (destination_metafile, destination_file_name); + real_remove_file_metadata (destination_metafile, destination_file_name); g_assert (get_file_node (destination_metafile, destination_file_name, FALSE) == NULL); source_node = get_file_node (source_metafile, source_file_name, FALSE); if (source_node != NULL) { - if (destination_metafile->details->is_read) { - node = xmlCopyNode (source_node, TRUE); - root = create_metafile_root (destination_metafile); - xmlAddChild (root, node); - xmlSetProp (node, "name", destination_file_name); - set_file_node_timestamp (node); - g_hash_table_insert (destination_metafile->details->node_hash, - xmlMemStrdup (destination_file_name), node); - } else { - /* FIXME bugzilla.gnome.org 46526: Copying data into a destination - * where the metafile was not yet read is not implemented. - */ - g_warning ("not copying metadata"); - } + node = xmlCopyNode (source_node, TRUE); + root = create_metafile_root (destination_metafile); + xmlAddChild (root, node); + xmlSetProp (node, "name", destination_file_name); + set_file_node_timestamp (node); + g_hash_table_insert (destination_metafile->details->node_hash, + xmlMemStrdup (destination_file_name), node); + directory_request_write_metafile (destination_metafile); } hash = source_metafile->details->changes; @@ -1458,6 +1645,34 @@ copy_file_metadata (NautilusMetafile *so changes); } } +} + +static void +copy_file_metadata (NautilusMetafile *source_metafile, + const char *source_file_name, + NautilusMetafile *destination_metafile, + const char *destination_file_name) +{ + char *source_file_uri; + char *destination_file_uri; + + g_return_if_fail (NAUTILUS_IS_METAFILE (source_metafile)); + g_return_if_fail (source_file_name != NULL); + g_return_if_fail (NAUTILUS_IS_METAFILE (destination_metafile)); + g_return_if_fail (destination_file_name != NULL); + + if (source_metafile->details->is_read + && destination_metafile->details->is_read) { + real_copy_file_metadata (source_metafile, + source_file_name, + destination_metafile, + destination_file_name); + } else { + nautilus_metadata_schedule_copy (source_metafile, + source_file_name, + destination_metafile, + destination_file_name); + } /* Copy the thumbnail for the file, if any. */ source_file_uri = metafile_get_file_uri (source_metafile, source_file_name); @@ -1468,14 +1683,13 @@ copy_file_metadata (NautilusMetafile *so } static void -remove_file_metadata (NautilusMetafile *metafile, - const char *file_name) +real_remove_file_metadata (NautilusMetafile *metafile, + const char *file_name) { gboolean found; gpointer key, value; xmlNode *file_node; GHashTable *hash; - char *file_uri; g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); g_return_if_fail (file_name != NULL); @@ -1512,6 +1726,22 @@ remove_file_metadata (NautilusMetafile * } } } +} + +static void +remove_file_metadata (NautilusMetafile *metafile, + const char *file_name) +{ + char *file_uri; + + g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); + g_return_if_fail (file_name != NULL); + + if (nautilus_metadata_has_scheduled_copy (metafile, file_name)) { + nautilus_metadata_schedule_removal (metafile, file_name); + } else { + real_remove_file_metadata (metafile, file_name); + } /* Delete the thumbnails for the file, if any. */ file_uri = metafile_get_file_uri (metafile, file_name); @@ -1588,6 +1818,9 @@ static void metafile_read_done (NautilusMetafile *metafile) { metafile_read_mark_done (metafile); + + nautilus_metadata_process_ready_copies (); + nautilus_metadata_process_ready_removals (); } static void
Attachment:
signature.asc
Description: This is a digitally signed message part