[PATCH 4/6] refs.c: lock cached_refs during for_each_ref

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Sven Verdoolaege <skimo@xxxxxxxxxx>

If the function called by for_each_ref modifies a ref in any way,
the cached_refs that for_each_ref was looping over would be
removed, resulting in undefined behavior.

This patch prevents the cached_refs from being removed
while for_each_ref is still iterating over them.

Signed-off-by: Sven Verdoolaege <skimo@xxxxxxxxxx>
---
 refs.c |   37 +++++++++++++++++++++++++++++++++----
 1 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/refs.c b/refs.c
index 4dc7e8b..e710903 100644
--- a/refs.c
+++ b/refs.c
@@ -153,6 +153,8 @@ static struct ref_list *sort_ref_list(struct ref_list *list)
 static struct cached_refs {
 	char did_loose;
 	char did_packed;
+	char is_locked;
+	char is_invalidated;
 	struct ref_list *loose;
 	struct ref_list *packed;
 } cached_refs;
@@ -170,6 +172,11 @@ static void invalidate_cached_refs(void)
 {
 	struct cached_refs *ca = &cached_refs;
 
+	if (ca->is_locked) {
+		ca->is_invalidated = 1;
+		return;
+	}
+
 	if (ca->did_loose && ca->loose)
 		free_ref_list(ca->loose);
 	if (ca->did_packed && ca->packed)
@@ -178,6 +185,24 @@ static void invalidate_cached_refs(void)
 	ca->did_loose = ca->did_packed = 0;
 }
 
+static void lock_cached_refs(void)
+{
+	struct cached_refs *ca = &cached_refs;
+
+	ca->is_locked = 1;
+}
+
+static void unlock_cached_refs(void)
+{
+	struct cached_refs *ca = &cached_refs;
+
+	ca->is_locked = 0;
+	if (ca->is_invalidated) {
+		invalidate_cached_refs();
+		ca->is_invalidated = 0;
+	}
+}
+
 static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
 {
 	struct ref_list *list = NULL;
@@ -518,10 +543,12 @@ int peel_ref(const char *ref, unsigned char *sha1)
 static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
 			   void *cb_data)
 {
-	int retval;
+	int retval = 0;
 	struct ref_list *packed = get_packed_refs();
 	struct ref_list *loose = get_loose_refs();
 
+	lock_cached_refs();
+
 	while (packed && loose) {
 		struct ref_list *entry;
 		int cmp = strcmp(packed->name, loose->name);
@@ -538,15 +565,17 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
 		}
 		retval = do_one_ref(base, fn, trim, cb_data, entry);
 		if (retval)
-			return retval;
+			goto out;
 	}
 
 	for (packed = packed ? packed : loose; packed; packed = packed->next) {
 		retval = do_one_ref(base, fn, trim, cb_data, packed);
 		if (retval)
-			return retval;
+			goto out;
 	}
-	return 0;
+ out:
+	unlock_cached_refs();
+	return retval;
 }
 
 int head_ref(each_ref_fn fn, void *cb_data)
-- 
1.5.3.rc0.100.ge60b4

-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux