Git fork

diff: improve lifecycle management of diff queues

The lifecycle management of diff queues is somewhat confusing:

- For most of the part this can be attributed to `DIFF_QUEUE_CLEAR()`,
which does not release any memory but rather initializes the queue,
only. This is in contrast to our common naming schema, where
"clearing" means that we release underlying memory and then
re-initialize the data structure such that it is ready to use.

- A second offender is `diff_free_queue()`, which does not free the
queue structure itself. It is rather a release-style function.

Refactor the code to make things less confusing. `DIFF_QUEUE_CLEAR()` is
replaced by `DIFF_QUEUE_INIT` and `diff_queue_init()`, while
`diff_free_queue()` is replaced by `diff_queue_release()`. While on it,
adapt callsites where we call `DIFF_QUEUE_CLEAR()` with the intent to
release underlying memory to instead call `diff_queue_clear()` to fix
memory leaks.

This memory leak is exposed by t4211, but plugging it alone does not
make the whole test suite pass.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Patrick Steinhardt and committed by
Junio C Hamano
a5aecb2c fdf972a9

+32 -47
+1 -7
bloom.c
··· 476 476 *last_slash = '\0'; 477 477 478 478 } while (*path); 479 - 480 - diff_free_filepair(diff_queued_diff.queue[i]); 481 479 } 482 480 483 481 if (hashmap_get_size(&pathmap) > settings->max_changed_paths) { ··· 508 506 cleanup: 509 507 hashmap_clear_and_free(&pathmap, struct pathmap_hash_entry, entry); 510 508 } else { 511 - for (i = 0; i < diff_queued_diff.nr; i++) 512 - diff_free_filepair(diff_queued_diff.queue[i]); 513 509 init_truncated_large_filter(filter, settings->hash_version); 514 510 515 511 if (computed) ··· 519 515 if (computed) 520 516 *computed |= BLOOM_COMPUTED; 521 517 522 - free(diff_queued_diff.queue); 523 - DIFF_QUEUE_CLEAR(&diff_queued_diff); 524 - 518 + diff_queue_clear(&diff_queued_diff); 525 519 return filter; 526 520 } 527 521
+12 -10
diff.c
··· 5983 5983 free(p); 5984 5984 } 5985 5985 5986 - void diff_free_queue(struct diff_queue_struct *q) 5986 + void diff_queue_init(struct diff_queue_struct *q) 5987 + { 5988 + struct diff_queue_struct blank = DIFF_QUEUE_INIT; 5989 + memcpy(q, &blank, sizeof(*q)); 5990 + } 5991 + 5992 + void diff_queue_clear(struct diff_queue_struct *q) 5987 5993 { 5988 5994 for (int i = 0; i < q->nr; i++) 5989 5995 diff_free_filepair(q->queue[i]); 5990 5996 free(q->queue); 5997 + diff_queue_init(q); 5991 5998 } 5992 5999 5993 6000 const char *diff_aligned_abbrev(const struct object_id *oid, int len) ··· 6551 6558 struct diff_queue_struct *q = &diff_queued_diff; 6552 6559 int result = diff_get_patch_id(options, oid, diff_header_only); 6553 6560 6554 - diff_free_queue(q); 6555 - DIFF_QUEUE_CLEAR(q); 6561 + diff_queue_clear(q); 6556 6562 6557 6563 return result; 6558 6564 } ··· 6835 6841 } 6836 6842 6837 6843 free_queue: 6838 - diff_free_queue(q); 6839 - DIFF_QUEUE_CLEAR(q); 6844 + diff_queue_clear(q); 6840 6845 diff_free(options); 6841 6846 6842 6847 /* ··· 6867 6872 { 6868 6873 int i; 6869 6874 struct diff_queue_struct *q = &diff_queued_diff; 6870 - struct diff_queue_struct outq; 6871 - 6872 - DIFF_QUEUE_CLEAR(&outq); 6875 + struct diff_queue_struct outq = DIFF_QUEUE_INIT; 6873 6876 6874 6877 if (!options->filter) 6875 6878 return; ··· 6962 6965 { 6963 6966 int i; 6964 6967 struct diff_queue_struct *q = &diff_queued_diff; 6965 - struct diff_queue_struct outq; 6966 - DIFF_QUEUE_CLEAR(&outq); 6968 + struct diff_queue_struct outq = DIFF_QUEUE_INIT; 6967 6969 6968 6970 for (i = 0; i < q->nr; i++) { 6969 6971 struct diff_filepair *p = q->queue[i];
+2 -6
diffcore-break.c
··· 131 131 void diffcore_break(struct repository *r, int break_score) 132 132 { 133 133 struct diff_queue_struct *q = &diff_queued_diff; 134 - struct diff_queue_struct outq; 134 + struct diff_queue_struct outq = DIFF_QUEUE_INIT; 135 135 136 136 /* When the filepair has this much edit (insert and delete), 137 137 * it is first considered to be a rewrite and broken into a ··· 177 177 break_score = DEFAULT_BREAK_SCORE; 178 178 if (!merge_score) 179 179 merge_score = DEFAULT_MERGE_SCORE; 180 - 181 - DIFF_QUEUE_CLEAR(&outq); 182 180 183 181 for (i = 0; i < q->nr; i++) { 184 182 struct diff_filepair *p = q->queue[i]; ··· 275 273 void diffcore_merge_broken(void) 276 274 { 277 275 struct diff_queue_struct *q = &diff_queued_diff; 278 - struct diff_queue_struct outq; 276 + struct diff_queue_struct outq = DIFF_QUEUE_INIT; 279 277 int i, j; 280 - 281 - DIFF_QUEUE_CLEAR(&outq); 282 278 283 279 for (i = 0; i < q->nr; i++) { 284 280 struct diff_filepair *p = q->queue[i];
+1 -3
diffcore-pickaxe.c
··· 182 182 regex_t *regexp, kwset_t kws, pickaxe_fn fn) 183 183 { 184 184 int i; 185 - struct diff_queue_struct outq; 186 - 187 - DIFF_QUEUE_CLEAR(&outq); 185 + struct diff_queue_struct outq = DIFF_QUEUE_INIT; 188 186 189 187 if (o->pickaxe_opts & DIFF_PICKAXE_ALL) { 190 188 /* Showing the whole changeset if needle exists */
+1 -2
diffcore-rename.c
··· 1388 1388 int detect_rename = options->detect_rename; 1389 1389 int minimum_score = options->rename_score; 1390 1390 struct diff_queue_struct *q = &diff_queued_diff; 1391 - struct diff_queue_struct outq; 1391 + struct diff_queue_struct outq = DIFF_QUEUE_INIT; 1392 1392 struct diff_score *mx; 1393 1393 int i, j, rename_count, skip_unmodified = 0; 1394 1394 int num_destinations, dst_cnt; ··· 1638 1638 * are recorded in rename_dst. The original list is still in *q. 1639 1639 */ 1640 1640 trace2_region_enter("diff", "write back to queue", options->repo); 1641 - DIFF_QUEUE_CLEAR(&outq); 1642 1641 for (i = 0; i < q->nr; i++) { 1643 1642 struct diff_filepair *p = q->queue[i]; 1644 1643 struct diff_filepair *pair_to_free = NULL;
+1 -2
diffcore-rotate.c
··· 10 10 void diffcore_rotate(struct diff_options *opt) 11 11 { 12 12 struct diff_queue_struct *q = &diff_queued_diff; 13 - struct diff_queue_struct outq; 13 + struct diff_queue_struct outq = DIFF_QUEUE_INIT; 14 14 int rotate_to, i; 15 15 16 16 if (!q->nr) ··· 31 31 return; 32 32 } 33 33 34 - DIFF_QUEUE_CLEAR(&outq); 35 34 rotate_to = i; 36 35 37 36 for (i = rotate_to; i < q->nr; i++)
+4 -6
diffcore.h
··· 153 153 int nr; 154 154 }; 155 155 156 - #define DIFF_QUEUE_CLEAR(q) \ 157 - do { \ 158 - (q)->queue = NULL; \ 159 - (q)->nr = (q)->alloc = 0; \ 160 - } while (0) 156 + #define DIFF_QUEUE_INIT { 0 } 157 + 158 + void diff_queue_init(struct diff_queue_struct *q); 159 + void diff_queue_clear(struct diff_queue_struct *q); 161 160 162 161 extern struct diff_queue_struct diff_queued_diff; 163 162 struct diff_filepair *diff_queue(struct diff_queue_struct *, 164 163 struct diff_filespec *, 165 164 struct diff_filespec *); 166 165 void diff_q(struct diff_queue_struct *, struct diff_filepair *); 167 - void diff_free_queue(struct diff_queue_struct *q); 168 166 169 167 /* dir_rename_relevance: the reason we want rename information for a dir */ 170 168 enum dir_rename_relevance {
+7 -8
line-log.c
··· 787 787 struct diff_queue_struct *src) 788 788 { 789 789 assert(src != dst); 790 - memcpy(dst, src, sizeof(struct diff_queue_struct)); 791 - DIFF_QUEUE_CLEAR(src); 790 + memcpy(dst, src, sizeof(*dst)); 791 + diff_queue_init(src); 792 792 } 793 793 794 794 static void filter_diffs_for_paths(struct line_log_data *range, int keep_deletions) 795 795 { 796 796 int i; 797 - struct diff_queue_struct outq; 798 - DIFF_QUEUE_CLEAR(&outq); 797 + struct diff_queue_struct outq = DIFF_QUEUE_INIT; 799 798 800 799 for (i = 0; i < diff_queued_diff.nr; i++) { 801 800 struct diff_filepair *p = diff_queued_diff.queue[i]; ··· 850 849 clear_pathspec(&opt->pathspec); 851 850 parse_pathspec_from_ranges(&opt->pathspec, range); 852 851 } 853 - DIFF_QUEUE_CLEAR(&diff_queued_diff); 852 + diff_queue_clear(&diff_queued_diff); 854 853 diff_tree_oid(parent_tree_oid, tree_oid, "", opt); 855 854 if (opt->detect_rename && diff_might_be_rename()) { 856 855 /* must look at the full tree diff to detect renames */ 857 856 clear_pathspec(&opt->pathspec); 858 - DIFF_QUEUE_CLEAR(&diff_queued_diff); 857 + diff_queue_clear(&diff_queued_diff); 859 858 860 859 diff_tree_oid(parent_tree_oid, tree_oid, "", opt); 861 860 ··· 1097 1096 static void free_diffqueues(int n, struct diff_queue_struct *dq) 1098 1097 { 1099 1098 for (int i = 0; i < n; i++) 1100 - diff_free_queue(&dq[i]); 1099 + diff_queue_clear(&dq[i]); 1101 1100 free(dq); 1102 1101 } 1103 1102 ··· 1200 1199 if (parent) 1201 1200 add_line_range(rev, parent, parent_range); 1202 1201 free_line_log_data(parent_range); 1203 - diff_free_queue(&queue); 1202 + diff_queue_clear(&queue); 1204 1203 return changed; 1205 1204 } 1206 1205
+2 -2
log-tree.c
··· 675 675 struct diff_queue_struct dq; 676 676 677 677 memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff)); 678 - DIFF_QUEUE_CLEAR(&diff_queued_diff); 678 + diff_queue_init(&diff_queued_diff); 679 679 680 680 fprintf_ln(opt->diffopt.file, "\n%s", opt->idiff_title); 681 681 show_interdiff(opt->idiff_oid1, opt->idiff_oid2, 2, ··· 694 694 }; 695 695 696 696 memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff)); 697 - DIFF_QUEUE_CLEAR(&diff_queued_diff); 697 + diff_queue_init(&diff_queued_diff); 698 698 699 699 fprintf_ln(opt->diffopt.file, "\n%s", opt->rdiff_title); 700 700 /*
+1 -1
merge-ort.c
··· 3536 3536 /* Free memory for renames->pairs[] and combined */ 3537 3537 for (s = MERGE_SIDE1; s <= MERGE_SIDE2; s++) { 3538 3538 free(renames->pairs[s].queue); 3539 - DIFF_QUEUE_CLEAR(&renames->pairs[s]); 3539 + diff_queue_init(&renames->pairs[s]); 3540 3540 } 3541 3541 for (i = 0; i < combined.nr; i++) 3542 3542 pool_diff_free_filepair(&opt->priv->pool, combined.queue[i]);