]> granicus.if.org Git - git/commitdiff
refs: manage lifetime of packed refs cache via reference counting
authorMichael Haggerty <mhagger@alum.mit.edu>
Thu, 20 Jun 2013 08:37:47 +0000 (10:37 +0200)
committerJunio C Hamano <gitster@pobox.com>
Thu, 20 Jun 2013 22:50:17 +0000 (15:50 -0700)
In struct packed_ref_cache, keep a count of the number of users of the
data structure.  Only free the packed ref cache when the reference
count goes to zero rather than when the packed ref cache is cleared.
This mechanism will be used to prevent the cache data structure from
being freed while it is being iterated over.

So far, only the reference in struct ref_cache::packed is counted;
other users will be adjusted in separate commits.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
refs.c

diff --git a/refs.c b/refs.c
index b3457996f519948b63d10775c41585d91c7ce6cd..80c172f469c4121bc0a72adb8ed47d083a36036e 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -809,6 +809,14 @@ static int is_refname_available(const char *refname, const char *oldrefname,
 struct packed_ref_cache {
        struct ref_entry *root;
 
+       /*
+        * Count of references to the data structure in this instance,
+        * including the pointer from ref_cache::packed if any.  The
+        * data will not be freed as long as the reference count is
+        * nonzero.
+        */
+       unsigned int referrers;
+
        /*
         * Iff the packed-refs file associated with this instance is
         * currently locked for writing, this points at the associated
@@ -836,14 +844,38 @@ static struct ref_cache {
 /* Lock used for the main packed-refs file: */
 static struct lock_file packlock;
 
+/*
+ * Increment the reference count of *packed_refs.
+ */
+static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+       packed_refs->referrers++;
+}
+
+/*
+ * Decrease the reference count of *packed_refs.  If it goes to zero,
+ * free *packed_refs and return true; otherwise return false.
+ */
+static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+       if (!--packed_refs->referrers) {
+               free_ref_entry(packed_refs->root);
+               free(packed_refs);
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
 static void clear_packed_ref_cache(struct ref_cache *refs)
 {
        if (refs->packed) {
-               if (refs->packed->lock)
+               struct packed_ref_cache *packed_refs = refs->packed;
+
+               if (packed_refs->lock)
                        die("internal error: packed-ref cache cleared while locked");
-               free_ref_entry(refs->packed->root);
-               free(refs->packed);
                refs->packed = NULL;
+               release_packed_ref_cache(packed_refs);
        }
 }
 
@@ -1024,6 +1056,7 @@ static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
                FILE *f;
 
                refs->packed = xcalloc(1, sizeof(*refs->packed));
+               acquire_packed_ref_cache(refs->packed);
                refs->packed->root = create_dir_entry(refs, "", 0, 0);
                if (*refs->name)
                        packed_refs_file = git_path_submodule(refs->name, "packed-refs");