Skip to content

Commit 8dd3a43

Browse files
committed
patch 7.4.2142
Problem: Leaking memory when redefining a function. Solution: Don't increment the function reference count when it's found by name. Don't remove the wrong function from the hashtab. More reference counting fixes.
1 parent ba96e9a commit 8dd3a43

3 files changed

Lines changed: 66 additions & 44 deletions

File tree

src/structs.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,7 @@ typedef struct
13271327
#endif
13281328
scid_T uf_script_ID; /* ID of script where function was defined,
13291329
used for s: variables */
1330-
int uf_refcount; /* for numbered function: reference count */
1330+
int uf_refcount; /* reference count, see func_name_refcount() */
13311331
funccall_T *uf_scoped; /* l: local variables for closure */
13321332
char_u uf_name[1]; /* name of function (actually longer); can
13331333
start with <SNR>123_ (<SNR> is K_SPECIAL
@@ -1365,9 +1365,11 @@ struct funccall_S
13651365
funccall_T *caller; /* calling function or NULL */
13661366

13671367
/* for closure */
1368-
int fc_refcount;
1368+
int fc_refcount; /* number of user functions that reference this
1369+
* funccal */
13691370
int fc_copyID; /* for garbage collection */
1370-
garray_T fc_funcs; /* list of ufunc_T* which refer this */
1371+
garray_T fc_funcs; /* list of ufunc_T* which keep a reference to
1372+
* "func" */
13711373
};
13721374

13731375
/*

src/userfunc.c

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515

1616
#if defined(FEAT_EVAL) || defined(PROTO)
1717
/* function flags */
18-
#define FC_ABORT 1 /* abort function on error */
19-
#define FC_RANGE 2 /* function accepts range */
20-
#define FC_DICT 4 /* Dict function, uses "self" */
21-
#define FC_CLOSURE 8 /* closure, uses outer scope variables */
22-
#define FC_DELETED 16 /* :delfunction used while uf_refcount > 0 */
18+
#define FC_ABORT 0x01 /* abort function on error */
19+
#define FC_RANGE 0x02 /* function accepts range */
20+
#define FC_DICT 0x04 /* Dict function, uses "self" */
21+
#define FC_CLOSURE 0x08 /* closure, uses outer scope variables */
22+
#define FC_DELETED 0x10 /* :delfunction used while uf_refcount > 0 */
23+
#define FC_REMOVED 0x20 /* function redefined while uf_refcount > 0 */
2324

2425
/* From user function to hashitem and back. */
2526
#define UF2HIKEY(fp) ((fp)->uf_name)
@@ -178,14 +179,18 @@ get_function_args(
178179
static int
179180
register_closure(ufunc_T *fp)
180181
{
181-
funccal_unref(fp->uf_scoped, NULL);
182+
if (fp->uf_scoped == current_funccal)
183+
/* no change */
184+
return OK;
185+
funccal_unref(fp->uf_scoped, fp);
182186
fp->uf_scoped = current_funccal;
183187
current_funccal->fc_refcount++;
188+
func_ptr_ref(current_funccal->func);
189+
184190
if (ga_grow(&current_funccal->fc_funcs, 1) == FAIL)
185191
return FAIL;
186192
((ufunc_T **)current_funccal->fc_funcs.ga_data)
187193
[current_funccal->fc_funcs.ga_len++] = fp;
188-
func_ptr_ref(current_funccal->func);
189194
return OK;
190195
}
191196

@@ -1024,60 +1029,56 @@ call_user_func(
10241029

10251030
/*
10261031
* Unreference "fc": decrement the reference count and free it when it
1027-
* becomes zero. If "fp" is not NULL, "fp" is detached from "fc".
1032+
* becomes zero. "fp" is detached from "fc".
10281033
*/
10291034
static void
10301035
funccal_unref(funccall_T *fc, ufunc_T *fp)
10311036
{
10321037
funccall_T **pfc;
10331038
int i;
1034-
int freed = FALSE;
10351039

10361040
if (fc == NULL)
10371041
return;
10381042

1039-
if (--fc->fc_refcount <= 0)
1040-
{
1043+
if (--fc->fc_refcount <= 0
1044+
&& fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
1045+
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
1046+
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
10411047
for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller)
10421048
{
10431049
if (fc == *pfc)
10441050
{
1045-
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
1046-
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
1047-
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
1048-
{
1049-
*pfc = fc->caller;
1050-
free_funccal(fc, TRUE);
1051-
freed = TRUE;
1052-
}
1053-
break;
1051+
*pfc = fc->caller;
1052+
free_funccal(fc, TRUE);
1053+
return;
10541054
}
10551055
}
1056-
}
1057-
if (!freed)
1056+
for (i = 0; i < fc->fc_funcs.ga_len; ++i)
10581057
{
1059-
func_ptr_unref(fc->func);
1060-
1061-
if (fp != NULL)
1062-
for (i = 0; i < fc->fc_funcs.ga_len; ++i)
1063-
{
1064-
if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp)
1065-
((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
1066-
}
1058+
if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp)
1059+
{
1060+
func_ptr_unref(fc->func);
1061+
((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
1062+
}
10671063
}
10681064
}
10691065

10701066
/*
10711067
* Remove the function from the function hashtable. If the function was
10721068
* deleted while it still has references this was already done.
1069+
* Return TRUE if the entry was deleted, FALSE if it wasn't found.
10731070
*/
1074-
static void
1071+
static int
10751072
func_remove(ufunc_T *fp)
10761073
{
10771074
hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp));
10781075

10791076
if (!HASHITEM_EMPTY(hi))
1077+
{
10801078
hash_remove(&func_hashtab, hi);
1079+
return TRUE;
1080+
}
1081+
return FALSE;
10811082
}
10821083

10831084
/*
@@ -1094,7 +1095,10 @@ func_free(ufunc_T *fp)
10941095
vim_free(fp->uf_tml_total);
10951096
vim_free(fp->uf_tml_self);
10961097
#endif
1097-
func_remove(fp);
1098+
/* only remove it when not done already, otherwise we would remove a newer
1099+
* version of the function */
1100+
if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0)
1101+
func_remove(fp);
10981102

10991103
funccal_unref(fp->uf_scoped, fp);
11001104

@@ -2158,6 +2162,7 @@ ex_function(exarg_T *eap)
21582162
/* This function is referenced somewhere, don't redefine it but
21592163
* create a new one. */
21602164
--fp->uf_refcount;
2165+
fp->uf_flags |= FC_REMOVED;
21612166
fp = NULL;
21622167
overwrite = TRUE;
21632168
}
@@ -2650,6 +2655,20 @@ get_user_func_name(expand_T *xp, int idx)
26502655

26512656
#endif /* FEAT_CMDL_COMPL */
26522657

2658+
/*
2659+
* There are two kinds of function names:
2660+
* 1. ordinary names, function defined with :function
2661+
* 2. numbered functions and lambdas
2662+
* For the first we only count the name stored in func_hashtab as a reference,
2663+
* using function() does not count as a reference, because the function is
2664+
* looked up by name.
2665+
*/
2666+
static int
2667+
func_name_refcount(char_u *name)
2668+
{
2669+
return isdigit(*name) || *name == '<';
2670+
}
2671+
26532672
/*
26542673
* ":delfunction {name}"
26552674
*/
@@ -2705,19 +2724,18 @@ ex_delfunction(exarg_T *eap)
27052724
}
27062725
else
27072726
{
2708-
/* Normal functions (not numbered functions and lambdas) have a
2727+
/* A normal function (not a numbered function or lambda) has a
27092728
* refcount of 1 for the entry in the hashtable. When deleting
2710-
* them and the refcount is more than one, it should be kept.
2711-
* Numbered functions and lambdas snould be kept if the refcount is
2729+
* it and the refcount is more than one, it should be kept.
2730+
* A numbered function and lambda snould be kept if the refcount is
27122731
* one or more. */
2713-
if (fp->uf_refcount > (isdigit(fp->uf_name[0])
2714-
|| fp->uf_name[0] == '<' ? 0 : 1))
2732+
if (fp->uf_refcount > (func_name_refcount(fp->uf_name) ? 0 : 1))
27152733
{
27162734
/* Function is still referenced somewhere. Don't free it but
27172735
* do remove it from the hashtable. */
2718-
func_remove(fp);
2736+
if (func_remove(fp))
2737+
fp->uf_refcount--;
27192738
fp->uf_flags |= FC_DELETED;
2720-
fp->uf_refcount--;
27212739
}
27222740
else
27232741
func_free(fp);
@@ -2734,7 +2752,7 @@ func_unref(char_u *name)
27342752
{
27352753
ufunc_T *fp = NULL;
27362754

2737-
if (name == NULL)
2755+
if (name == NULL || !func_name_refcount(name))
27382756
return;
27392757
fp = find_func(name);
27402758
if (fp == NULL && isdigit(*name))
@@ -2777,7 +2795,7 @@ func_ref(char_u *name)
27772795
{
27782796
ufunc_T *fp;
27792797

2780-
if (name == NULL)
2798+
if (name == NULL || !func_name_refcount(name))
27812799
return;
27822800
fp = find_func(name);
27832801
if (fp != NULL)

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,8 @@ static char *(features[]) =
763763

764764
static int included_patches[] =
765765
{ /* Add new patch number below this line */
766+
/**/
767+
2142,
766768
/**/
767769
2141,
768770
/**/

0 commit comments

Comments
 (0)