[vim] 31/139: patch 7.4.1719 Problem: Leaking memory when there is a cycle involving a job and a partial. Solution: Add a copyID to job and channel. Set references in items referred by them. Go through all jobs and channels to find unreferenced items. Also, decrement reference counts when garbage collecting.

James McCoy jamessan at debian.org
Fri May 6 03:59:57 UTC 2016


This is an automated email from the git hooks/post-receive script.

jamessan pushed a commit to branch debian/sid
in repository vim.

commit 107e1eef1df3b786ad3ad49fbdb9e058649303b5
Author: Bram Moolenaar <Bram at vim.org>
Date:   Fri Apr 8 17:07:19 2016 +0200

    patch 7.4.1719
    Problem:    Leaking memory when there is a cycle involving a job and a
                partial.
    Solution:   Add a copyID to job and channel.  Set references in items referred
                by them.  Go through all jobs and channels to find unreferenced
                items.  Also, decrement reference counts when garbage collecting.
---
 src/channel.c                | 138 ++++++++++++++++---
 src/eval.c                   | 321 +++++++++++++++++++++++++++++--------------
 src/globals.h                |   2 +
 src/ops.c                    |   4 +-
 src/proto/channel.pro        |   5 +-
 src/proto/eval.pro           |   8 +-
 src/regexp.c                 |   2 +-
 src/structs.h                |   5 +-
 src/tag.c                    |   4 +-
 src/testdir/test_partial.vim |  19 ++-
 src/version.c                |   2 +
 11 files changed, 377 insertions(+), 133 deletions(-)

diff --git a/src/channel.c b/src/channel.c
index 7b811db..83d057d 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -368,6 +368,39 @@ channel_still_useful(channel_T *channel)
 }
 
 /*
+ * Close a channel and free all its resources.
+ */
+    static void
+channel_free_contents(channel_T *channel)
+{
+    channel_close(channel, TRUE);
+    channel_clear(channel);
+    ch_log(channel, "Freeing channel");
+}
+
+    static void
+channel_free_channel(channel_T *channel)
+{
+    if (channel->ch_next != NULL)
+	channel->ch_next->ch_prev = channel->ch_prev;
+    if (channel->ch_prev == NULL)
+	first_channel = channel->ch_next;
+    else
+	channel->ch_prev->ch_next = channel->ch_next;
+    vim_free(channel);
+}
+
+    static void
+channel_free(channel_T *channel)
+{
+    if (!in_free_unref_items)
+    {
+	channel_free_contents(channel);
+	channel_free_channel(channel);
+    }
+}
+
+/*
  * Close a channel and free all its resources if there is no further action
  * possible, there is no callback to be invoked or the associated job was
  * killed.
@@ -397,22 +430,39 @@ channel_unref(channel_T *channel)
     return FALSE;
 }
 
-/*
- * Close a channel and free all its resources.
- */
+    int
+free_unused_channels_contents(int copyID, int mask)
+{
+    int		did_free = FALSE;
+    channel_T	*ch;
+
+    for (ch = first_channel; ch != NULL; ch = ch->ch_next)
+	if ((ch->ch_copyID & mask) != (copyID & mask))
+	{
+	    /* Free the channel and ordinary items it contains, but don't
+	     * recurse into Lists, Dictionaries etc. */
+	    channel_free_contents(ch);
+	    did_free = TRUE;
+	}
+    return did_free;
+}
+
     void
-channel_free(channel_T *channel)
+free_unused_channels(int copyID, int mask)
 {
-    channel_close(channel, TRUE);
-    channel_clear(channel);
-    ch_log(channel, "Freeing channel");
-    if (channel->ch_next != NULL)
-	channel->ch_next->ch_prev = channel->ch_prev;
-    if (channel->ch_prev == NULL)
-	first_channel = channel->ch_next;
-    else
-	channel->ch_prev->ch_next = channel->ch_next;
-    vim_free(channel);
+    channel_T	*ch;
+    channel_T	*ch_next;
+
+    for (ch = first_channel; ch != NULL; ch = ch_next)
+    {
+	ch_next = ch->ch_next;
+	if ((ch->ch_copyID & mask) != (copyID & mask))
+	{
+	    /* Free the channel and ordinary items it contains, but don't
+	     * recurse into Lists, Dictionaries etc. */
+	    channel_free_channel(ch);
+	}
+    }
 }
 
 #if defined(FEAT_GUI) || defined(PROTO)
@@ -2457,6 +2507,7 @@ channel_clear(channel_T *channel)
     channel_clear_one(channel, PART_SOCK);
     channel_clear_one(channel, PART_OUT);
     channel_clear_one(channel, PART_ERR);
+    /* there is no callback or queue for PART_IN */
     vim_free(channel->ch_callback);
     channel->ch_callback = NULL;
     partial_unref(channel->ch_partial);
@@ -3913,7 +3964,7 @@ get_channel_arg(typval_T *tv, int check_open)
 static job_T *first_job = NULL;
 
     static void
-job_free(job_T *job)
+job_free_contents(job_T *job)
 {
     ch_log(job->jv_channel, "Freeing job");
     if (job->jv_channel != NULL)
@@ -3928,19 +3979,33 @@ job_free(job_T *job)
     }
     mch_clear_job(job);
 
+    vim_free(job->jv_stoponexit);
+    vim_free(job->jv_exit_cb);
+    partial_unref(job->jv_exit_partial);
+}
+
+    static void
+job_free_job(job_T *job)
+{
     if (job->jv_next != NULL)
 	job->jv_next->jv_prev = job->jv_prev;
     if (job->jv_prev == NULL)
 	first_job = job->jv_next;
     else
 	job->jv_prev->jv_next = job->jv_next;
-
-    vim_free(job->jv_stoponexit);
-    vim_free(job->jv_exit_cb);
-    partial_unref(job->jv_exit_partial);
     vim_free(job);
 }
 
+    static void
+job_free(job_T *job)
+{
+    if (!in_free_unref_items)
+    {
+	job_free_contents(job);
+	job_free_job(job);
+    }
+}
+
     void
 job_unref(job_T *job)
 {
@@ -3964,6 +4029,41 @@ job_unref(job_T *job)
     }
 }
 
+    int
+free_unused_jobs_contents(int copyID, int mask)
+{
+    int		did_free = FALSE;
+    job_T	*job;
+
+    for (job = first_job; job != NULL; job = job->jv_next)
+	if ((job->jv_copyID & mask) != (copyID & mask))
+	{
+	    /* Free the channel and ordinary items it contains, but don't
+	     * recurse into Lists, Dictionaries etc. */
+	    job_free_contents(job);
+	    did_free = TRUE;
+    }
+    return did_free;
+}
+
+    void
+free_unused_jobs(int copyID, int mask)
+{
+    job_T	*job;
+    job_T	*job_next;
+
+    for (job = first_job; job != NULL; job = job_next)
+    {
+	job_next = job->jv_next;
+	if ((job->jv_copyID & mask) != (copyID & mask))
+	{
+	    /* Free the channel and ordinary items it contains, but don't
+	     * recurse into Lists, Dictionaries etc. */
+	    job_free_job(job);
+	}
+    }
+}
+
 /*
  * Allocate a job.  Sets the refcount to one and sets options default.
  */
diff --git a/src/eval.c b/src/eval.c
index 3dc3e21..1c5a316 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -430,6 +430,8 @@ static int get_option_tv(char_u **arg, typval_T *rettv, int evaluate);
 static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);
 static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);
 static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate);
+static void list_free_contents(list_T  *l);
+static void list_free_list(list_T  *l);
 static long list_len(list_T *l);
 static int list_equal(list_T *l1, list_T *l2, int ic, int recursive);
 static int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
@@ -459,6 +461,9 @@ static int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, lin
 static void emsg_funcname(char *ermsg, char_u *name);
 static int non_zero_arg(typval_T *argvars);
 
+static void dict_free_contents(dict_T *d);
+static void dict_free_dict(dict_T *d);
+
 #ifdef FEAT_FLOAT
 static void f_abs(typval_T *argvars, typval_T *rettv);
 static void f_acos(typval_T *argvars, typval_T *rettv);
@@ -5589,7 +5594,7 @@ eval_index(
 		    {
 			if (list_append_tv(l, &item->li_tv) == FAIL)
 			{
-			    list_free(l, TRUE);
+			    list_free(l);
 			    return FAIL;
 			}
 			item = item->li_next;
@@ -5930,20 +5935,14 @@ get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
 }
 
     static void
-partial_free(partial_T *pt, int recursive)
+partial_free(partial_T *pt)
 {
     int i;
 
     for (i = 0; i < pt->pt_argc; ++i)
-    {
-	typval_T *tv = &pt->pt_argv[i];
-
-	if (recursive || (tv->v_type != VAR_DICT && tv->v_type != VAR_LIST))
-	    clear_tv(tv);
-    }
+	clear_tv(&pt->pt_argv[i]);
     vim_free(pt->pt_argv);
-    if (recursive)
-	dict_unref(pt->pt_dict);
+    dict_unref(pt->pt_dict);
     func_unref(pt->pt_name);
     vim_free(pt->pt_name);
     vim_free(pt);
@@ -5957,27 +5956,7 @@ partial_free(partial_T *pt, int recursive)
 partial_unref(partial_T *pt)
 {
     if (pt != NULL && --pt->pt_refcount <= 0)
-	partial_free(pt, TRUE);
-}
-
-/*
- * Like clear_tv(), but do not free lists or dictionaries.
- * This is when called via free_unref_items().
- */
-    static void
-clear_tv_no_recurse(typval_T *tv)
-{
-    if (tv->v_type == VAR_PARTIAL)
-    {
-	partial_T *pt = tv->vval.v_partial;
-
-	/* We unref the partial but not the dict or any list it
-	 * refers to. */
-	if (pt != NULL && --pt->pt_refcount == 0)
-	    partial_free(pt, FALSE);
-    }
-    else if (tv->v_type != VAR_LIST && tv->v_type != VAR_DICT)
-	clear_tv(tv);
+	partial_free(pt);
 }
 
 /*
@@ -6031,7 +6010,7 @@ get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
 	EMSG2(_("E697: Missing end of List ']': %s"), *arg);
 failret:
 	if (evaluate)
-	    list_free(l, TRUE);
+	    list_free(l);
 	return FAIL;
     }
 
@@ -6095,20 +6074,30 @@ rettv_list_alloc(typval_T *rettv)
 list_unref(list_T *l)
 {
     if (l != NULL && --l->lv_refcount <= 0)
-	list_free(l, TRUE);
+	list_free(l);
 }
 
 /*
  * Free a list, including all non-container items it points to.
  * Ignores the reference count.
  */
-    void
-list_free(
-    list_T  *l,
-    int	    recurse)	/* Free Lists and Dictionaries recursively. */
+    static void
+list_free_contents(list_T  *l)
 {
     listitem_T *item;
 
+    for (item = l->lv_first; item != NULL; item = l->lv_first)
+    {
+	/* Remove the item before deleting it. */
+	l->lv_first = item->li_next;
+	clear_tv(&item->li_tv);
+	vim_free(item);
+    }
+}
+
+    static void
+list_free_list(list_T  *l)
+{
     /* Remove the list from the list of lists for garbage collection. */
     if (l->lv_used_prev == NULL)
 	first_list = l->lv_used_next;
@@ -6117,17 +6106,17 @@ list_free(
     if (l->lv_used_next != NULL)
 	l->lv_used_next->lv_used_prev = l->lv_used_prev;
 
-    for (item = l->lv_first; item != NULL; item = l->lv_first)
+    vim_free(l);
+}
+
+    void
+list_free(list_T *l)
+{
+    if (!in_free_unref_items)
     {
-	/* Remove the item before deleting it. */
-	l->lv_first = item->li_next;
-	if (recurse)
-	    clear_tv(&item->li_tv);
-	else
-	    clear_tv_no_recurse(&item->li_tv);
-	vim_free(item);
+	list_free_contents(l);
+	list_free_list(l);
     }
-    vim_free(l);
 }
 
 /*
@@ -7016,7 +7005,7 @@ garbage_collect(void)
 #endif
 
 #ifdef FEAT_JOB_CHANNEL
-    abort = abort || set_ref_in_channel(copyID);
+//    abort = abort || set_ref_in_channel(copyID);
 #endif
 
     if (!abort)
@@ -7056,7 +7045,7 @@ garbage_collect(void)
 }
 
 /*
- * Free lists, dictionaries and jobs that are no longer referenced.
+ * Free lists, dictionaries, channels and jobs that are no longer referenced.
  */
     static int
 free_unref_items(int copyID)
@@ -7065,29 +7054,66 @@ free_unref_items(int copyID)
     list_T	*ll, *ll_next;
     int		did_free = FALSE;
 
+    /* Let all "free" functions know that we are here.  This means no
+     * dictionaries, lists, channels or jobs are to be freed, because we will
+     * do that here. */
+    in_free_unref_items = TRUE;
+
+    /*
+     * PASS 1: free the contents of the items.  We don't free the items
+     * themselves yet, so that it is possible to decrement refcount counters
+     */
+
     /*
      * Go through the list of dicts and free items without the copyID.
      */
-    for (dd = first_dict; dd != NULL; )
-    {
-	dd_next = dd->dv_used_next;
+    for (dd = first_dict; dd != NULL; dd = dd->dv_used_next)
 	if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
 	{
 	    /* Free the Dictionary and ordinary items it contains, but don't
 	     * recurse into Lists and Dictionaries, they will be in the list
 	     * of dicts or list of lists. */
-	    dict_free(dd, FALSE);
+	    dict_free_contents(dd);
 	    did_free = TRUE;
 	}
-	dd = dd_next;
-    }
 
     /*
      * Go through the list of lists and free items without the copyID.
      * But don't free a list that has a watcher (used in a for loop), these
      * are not referenced anywhere.
      */
-    for (ll = first_list; ll != NULL; )
+    for (ll = first_list; ll != NULL; ll = ll->lv_used_next)
+	if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
+						      && ll->lv_watch == NULL)
+	{
+	    /* Free the List and ordinary items it contains, but don't recurse
+	     * into Lists and Dictionaries, they will be in the list of dicts
+	     * or list of lists. */
+	    list_free_contents(ll);
+	    did_free = TRUE;
+	}
+
+#ifdef FEAT_JOB_CHANNEL
+    /* Go through the list of jobs and free items without the copyID. This
+     * must happen before doing channels, because jobs refer to channels, but
+     * the reference from the channel to the job isn't tracked. */
+    did_free |= free_unused_jobs_contents(copyID, COPYID_MASK);
+
+    /* Go through the list of channels and free items without the copyID.  */
+    did_free |= free_unused_channels_contents(copyID, COPYID_MASK);
+#endif
+
+    /*
+     * PASS 2: free the items themselves.
+     */
+    for (dd = first_dict; dd != NULL; dd = dd_next)
+    {
+	dd_next = dd->dv_used_next;
+	if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
+	    dict_free_dict(dd);
+    }
+
+    for (ll = first_list; ll != NULL; ll = ll_next)
     {
 	ll_next = ll->lv_used_next;
 	if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
@@ -7096,12 +7122,22 @@ free_unref_items(int copyID)
 	    /* Free the List and ordinary items it contains, but don't recurse
 	     * into Lists and Dictionaries, they will be in the list of dicts
 	     * or list of lists. */
-	    list_free(ll, FALSE);
-	    did_free = TRUE;
+	    list_free_list(ll);
 	}
-	ll = ll_next;
     }
 
+#ifdef FEAT_JOB_CHANNEL
+    /* Go through the list of jobs and free items without the copyID. This
+     * must happen before doing channels, because jobs refer to channels, but
+     * the reference from the channel to the job isn't tracked. */
+    free_unused_jobs(copyID, COPYID_MASK);
+
+    /* Go through the list of channels and free items without the copyID.  */
+    free_unused_channels(copyID, COPYID_MASK);
+#endif
+
+    in_free_unref_items = FALSE;
+
     return did_free;
 }
 
@@ -7204,18 +7240,12 @@ set_ref_in_item(
     ht_stack_T	    **ht_stack,
     list_stack_T    **list_stack)
 {
-    dict_T	*dd;
-    list_T	*ll;
     int		abort = FALSE;
 
-    if (tv->v_type == VAR_DICT || tv->v_type == VAR_PARTIAL)
+    if (tv->v_type == VAR_DICT)
     {
-	if (tv->v_type == VAR_DICT)
-	    dd = tv->vval.v_dict;
-	else if (tv->vval.v_partial != NULL)
-	    dd = tv->vval.v_partial->pt_dict;
-	else
-	    dd = NULL;
+	dict_T	*dd = tv->vval.v_dict;
+
 	if (dd != NULL && dd->dv_copyID != copyID)
 	{
 	    /* Didn't see this dict yet. */
@@ -7237,20 +7267,11 @@ set_ref_in_item(
 		}
 	    }
 	}
-	if (tv->v_type == VAR_PARTIAL)
-	{
-	    partial_T	*pt = tv->vval.v_partial;
-	    int		i;
-
-	    if (pt != NULL)
-		for (i = 0; i < pt->pt_argc; ++i)
-		    abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
-							ht_stack, list_stack);
-	}
     }
     else if (tv->v_type == VAR_LIST)
     {
-	ll = tv->vval.v_list;
+	list_T	*ll = tv->vval.v_list;
+
 	if (ll != NULL && ll->lv_copyID != copyID)
 	{
 	    /* Didn't see this list yet. */
@@ -7274,6 +7295,96 @@ set_ref_in_item(
 	    }
 	}
     }
+    else if (tv->v_type == VAR_PARTIAL)
+    {
+	partial_T	*pt = tv->vval.v_partial;
+	int		i;
+
+	/* A partial does not have a copyID, because it cannot contain itself.
+	 */
+	if (pt != NULL)
+	{
+	    if (pt->pt_dict != NULL)
+	    {
+		typval_T dtv;
+
+		dtv.v_type = VAR_DICT;
+		dtv.vval.v_dict = pt->pt_dict;
+		set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+	    }
+
+	    for (i = 0; i < pt->pt_argc; ++i)
+		abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
+							ht_stack, list_stack);
+	}
+    }
+#ifdef FEAT_JOB_CHANNEL
+    else if (tv->v_type == VAR_JOB)
+    {
+	job_T	    *job = tv->vval.v_job;
+	typval_T    dtv;
+
+	if (job != NULL && job->jv_copyID != copyID)
+	{
+	    if (job->jv_channel != NULL)
+	    {
+		dtv.v_type = VAR_CHANNEL;
+		dtv.vval.v_channel = job->jv_channel;
+		set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+	    }
+	    if (job->jv_exit_partial != NULL)
+	    {
+		dtv.v_type = VAR_PARTIAL;
+		dtv.vval.v_partial = job->jv_exit_partial;
+		set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+	    }
+	}
+    }
+    else if (tv->v_type == VAR_CHANNEL)
+    {
+	channel_T   *ch =tv->vval.v_channel;
+	int	    part;
+	typval_T    dtv;
+	jsonq_T	    *jq;
+	cbq_T	    *cq;
+
+	if (ch != NULL && ch->ch_copyID != copyID)
+	{
+	    for (part = PART_SOCK; part <= PART_IN; ++part)
+	    {
+		for (jq = ch->ch_part[part].ch_json_head.jq_next; jq != NULL;
+							     jq = jq->jq_next)
+		    set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack);
+		for (cq = ch->ch_part[part].ch_cb_head.cq_next; cq != NULL;
+							     cq = cq->cq_next)
+		    if (cq->cq_partial != NULL)
+		    {
+			dtv.v_type = VAR_PARTIAL;
+			dtv.vval.v_partial = cq->cq_partial;
+			set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+		    }
+		if (ch->ch_part[part].ch_partial != NULL)
+		{
+		    dtv.v_type = VAR_PARTIAL;
+		    dtv.vval.v_partial = ch->ch_part[part].ch_partial;
+		    set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+		}
+	    }
+	    if (ch->ch_partial != NULL)
+	    {
+		dtv.v_type = VAR_PARTIAL;
+		dtv.vval.v_partial = ch->ch_partial;
+		set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+	    }
+	    if (ch->ch_close_partial != NULL)
+	    {
+		dtv.v_type = VAR_PARTIAL;
+		dtv.vval.v_partial = ch->ch_close_partial;
+		set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+	    }
+	}
+    }
+#endif
     return abort;
 }
 
@@ -7332,30 +7443,20 @@ rettv_dict_alloc(typval_T *rettv)
 dict_unref(dict_T *d)
 {
     if (d != NULL && --d->dv_refcount <= 0)
-	dict_free(d, TRUE);
+	dict_free(d);
 }
 
 /*
  * Free a Dictionary, including all non-container items it contains.
  * Ignores the reference count.
  */
-    void
-dict_free(
-    dict_T  *d,
-    int	    recurse)	/* Free Lists and Dictionaries recursively. */
+    static void
+dict_free_contents(dict_T *d)
 {
     int		todo;
     hashitem_T	*hi;
     dictitem_T	*di;
 
-    /* Remove the dict from the list of dicts for garbage collection. */
-    if (d->dv_used_prev == NULL)
-	first_dict = d->dv_used_next;
-    else
-	d->dv_used_prev->dv_used_next = d->dv_used_next;
-    if (d->dv_used_next != NULL)
-	d->dv_used_next->dv_used_prev = d->dv_used_prev;
-
     /* Lock the hashtab, we don't want it to resize while freeing items. */
     hash_lock(&d->dv_hashtab);
     todo = (int)d->dv_hashtab.ht_used;
@@ -7367,18 +7468,37 @@ dict_free(
 	     * something recursive causing trouble. */
 	    di = HI2DI(hi);
 	    hash_remove(&d->dv_hashtab, hi);
-	    if (recurse)
-		clear_tv(&di->di_tv);
-	    else
-		clear_tv_no_recurse(&di->di_tv);
+	    clear_tv(&di->di_tv);
 	    vim_free(di);
 	    --todo;
 	}
     }
     hash_clear(&d->dv_hashtab);
+}
+
+    static void
+dict_free_dict(dict_T *d)
+{
+    /* Remove the dict from the list of dicts for garbage collection. */
+    if (d->dv_used_prev == NULL)
+	first_dict = d->dv_used_next;
+    else
+	d->dv_used_prev->dv_used_next = d->dv_used_next;
+    if (d->dv_used_next != NULL)
+	d->dv_used_next->dv_used_prev = d->dv_used_prev;
     vim_free(d);
 }
 
+    void
+dict_free(dict_T *d)
+{
+    if (!in_free_unref_items)
+    {
+	dict_free_contents(d);
+	dict_free_dict(d);
+    }
+}
+
 /*
  * Allocate a Dictionary item.
  * The "key" is copied to the new item.
@@ -7827,7 +7947,7 @@ get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
 	EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
 failret:
 	if (evaluate)
-	    dict_free(d, TRUE);
+	    dict_free(d);
 	return FAIL;
     }
 
@@ -7842,9 +7962,6 @@ failret:
     return OK;
 }
 
-#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
-#endif
-
     static char *
 get_var_special_name(int nr)
 {
@@ -15391,7 +15508,7 @@ find_some_match(typval_T *argvars, typval_T *rettv, int type)
 		    || list_append_number(rettv->vval.v_list,
 					    (varnumber_T)-1) == FAIL))
 	{
-		list_free(rettv->vval.v_list, TRUE);
+		list_free(rettv->vval.v_list);
 		rettv->vval.v_list = NULL;
 		goto theend;
 	}
@@ -16488,7 +16605,7 @@ f_readfile(typval_T *argvars, typval_T *rettv)
 
     if (failed)
     {
-	list_free(rettv->vval.v_list, TRUE);
+	list_free(rettv->vval.v_list);
 	/* readfile doc says an empty list is returned on error */
 	rettv->vval.v_list = list_alloc();
     }
@@ -20070,7 +20187,7 @@ errret:
     if (res != NULL)
 	vim_free(res);
     if (list != NULL)
-	list_free(list, TRUE);
+	list_free(list);
 }
 
 /*
diff --git a/src/globals.h b/src/globals.h
index 05dec4a..3278ad9 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1619,6 +1619,8 @@ EXTERN int  alloc_fail_countdown INIT(= -1);
 EXTERN int  alloc_fail_repeat INIT(= 0);
 
 EXTERN int  disable_char_avail_for_testing INIT(= 0);
+
+EXTERN int  in_free_unref_items INIT(= FALSE);
 #endif
 
 /*
diff --git a/src/ops.c b/src/ops.c
index b1910d9..0584877 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -6391,7 +6391,7 @@ getreg_wrap_one_line(char_u *s, int flags)
 	{
 	    if (list_append_string(list, NULL, -1) == FAIL)
 	    {
-		list_free(list, TRUE);
+		list_free(list);
 		return NULL;
 	    }
 	    list->lv_first->li_tv.vval.v_string = s;
@@ -6465,7 +6465,7 @@ get_reg_contents(int regname, int flags)
 		error = TRUE;
 	if (error)
 	{
-	    list_free(list, TRUE);
+	    list_free(list);
 	    return NULL;
 	}
 	return (char_u *)list;
diff --git a/src/proto/channel.pro b/src/proto/channel.pro
index e4ef003..d5114cb 100644
--- a/src/proto/channel.pro
+++ b/src/proto/channel.pro
@@ -5,7 +5,8 @@ void ch_log(channel_T *ch, char *msg);
 void ch_logs(channel_T *ch, char *msg, char *name);
 channel_T *add_channel(void);
 int channel_unref(channel_T *channel);
-void channel_free(channel_T *channel);
+int free_unused_channels_contents(int copyID, int mask);
+void free_unused_channels(int copyID, int mask);
 void channel_gui_register_all(void);
 channel_T *channel_open(char *hostname, int port_in, int waittime, void (*nb_close_cb)(void));
 channel_T *channel_open_func(typval_T *argvars);
@@ -50,6 +51,8 @@ void free_job_options(jobopt_T *opt);
 int get_job_options(typval_T *tv, jobopt_T *opt, int supported);
 channel_T *get_channel_arg(typval_T *tv, int check_open);
 void job_unref(job_T *job);
+int free_unused_jobs_contents(int copyID, int mask);
+void free_unused_jobs(int copyID, int mask);
 void job_set_options(job_T *job, jobopt_T *opt);
 void job_stop_on_exit(void);
 void job_check_ended(void);
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index 1de7a6d..38392b9 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -45,10 +45,12 @@ void ex_lockvar(exarg_T *eap);
 int do_unlet(char_u *name, int forceit);
 void del_menutrans_vars(void);
 char_u *get_user_var_name(expand_T *xp, int idx);
+void partial_unref(partial_T *pt);
 list_T *list_alloc(void);
 int rettv_list_alloc(typval_T *rettv);
 void list_unref(list_T *l);
-void list_free(list_T *l, int recurse);
+void list_free_internal(list_T *l);
+void list_free(list_T *l);
 listitem_T *listitem_alloc(void);
 void listitem_free(listitem_T *item);
 void listitem_remove(list_T *l, listitem_T *item);
@@ -71,7 +73,8 @@ int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_
 dict_T *dict_alloc(void);
 int rettv_dict_alloc(typval_T *rettv);
 void dict_unref(dict_T *d);
-void dict_free(dict_T *d, int recurse);
+void dict_free_internal(dict_T *d);
+void dict_free(dict_T *d);
 dictitem_T *dictitem_alloc(char_u *key);
 void dictitem_free(dictitem_T *item);
 int dict_add(dict_T *d, dictitem_T *item);
@@ -87,7 +90,6 @@ int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typva
 buf_T *buflist_find_by_name(char_u *name, int curtab_only);
 int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv);
 void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
-void partial_unref(partial_T *pt);
 void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
 float_T vim_round(float_T f);
 long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit);
diff --git a/src/regexp.c b/src/regexp.c
index b20e9c5..fada9fe 100644
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -7910,7 +7910,7 @@ reg_submatch_list(int no)
 
     if (error)
     {
-	list_free(list, TRUE);
+	list_free(list);
 	return NULL;
     }
     return list;
diff --git a/src/structs.h b/src/structs.h
index e753860..adee3e8 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1290,6 +1290,8 @@ struct jobvar_S
     buf_T	*jv_in_buf;	/* buffer from "in-name" */
 
     int		jv_refcount;	/* reference count */
+    int		jv_copyID;
+
     channel_T	*jv_channel;	/* channel for I/O, reference counted */
 };
 
@@ -1425,11 +1427,12 @@ struct channel_S {
 
     job_T	*ch_job;	/* Job that uses this channel; this does not
 				 * count as a reference to avoid a circular
-				 * reference. */
+				 * reference, the job refers to the channel. */
     int		ch_job_killed;	/* TRUE when there was a job and it was killed
 				 * or we know it died. */
 
     int		ch_refcount;	/* reference count */
+    int		ch_copyID;
 };
 
 #define JO_MODE		    0x0001	/* channel mode */
diff --git a/src/tag.c b/src/tag.c
index 1541259..4224c6f 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -792,7 +792,7 @@ do_tag(
 		    vim_free(cmd);
 		    vim_free(fname);
 		    if (list != NULL)
-			list_free(list, TRUE);
+			list_free(list);
 		    goto end_do_tag;
 		}
 
@@ -919,7 +919,7 @@ do_tag(
 		vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
 		set_errorlist(curwin, list, ' ', IObuff);
 
-		list_free(list, TRUE);
+		list_free(list);
 		vim_free(fname);
 		vim_free(cmd);
 
diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim
index 2d53e82..404bb74 100644
--- a/src/testdir/test_partial.vim
+++ b/src/testdir/test_partial.vim
@@ -221,7 +221,7 @@ func Test_bind_in_python()
   endif
 endfunc
 
-" This causes double free on exit if EXITFREE is defined.
+" This caused double free on exit if EXITFREE is defined.
 func Test_cyclic_list_arg()
   let l = []
   let Pt = function('string', [l])
@@ -230,7 +230,7 @@ func Test_cyclic_list_arg()
   unlet Pt
 endfunc
 
-" This causes double free on exit if EXITFREE is defined.
+" This caused double free on exit if EXITFREE is defined.
 func Test_cyclic_dict_arg()
   let d = {}
   let Pt = function('string', [d])
@@ -238,3 +238,18 @@ func Test_cyclic_dict_arg()
   unlet d
   unlet Pt
 endfunc
+
+func Ignored(job1, job2, status)
+endfunc
+
+func Test_cycle_partial_job()
+  let job = job_start('echo')
+  call job_setoptions(job, {'exit_cb': function('Ignored', [job])})
+  unlet job
+endfunc
+
+func Test_ref_job_partial_dict()
+  let g:ref_job = job_start('echo')
+  let d = {'a': 'b'}
+  call job_setoptions(g:ref_job, {'exit_cb': function('string', [], d)})
+endfunc
diff --git a/src/version.c b/src/version.c
index a808bb7..dd50f98 100644
--- a/src/version.c
+++ b/src/version.c
@@ -749,6 +749,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1719,
+/**/
     1718,
 /**/
     1717,

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-vim/vim.git



More information about the pkg-vim-maintainers mailing list