]> granicus.if.org Git - vim/commitdiff
patch 7.4.1719 v7.4.1719
authorBram Moolenaar <Bram@vim.org>
Fri, 8 Apr 2016 15:07:19 +0000 (17:07 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 8 Apr 2016 15:07:19 +0000 (17:07 +0200)
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
src/eval.c
src/globals.h
src/ops.c
src/proto/channel.pro
src/proto/eval.pro
src/regexp.c
src/structs.h
src/tag.c
src/testdir/test_partial.vim
src/version.c

index 7b811dbce2b0491ef830e8ec918902e2d1954f13..83d057dbb089fd84859a35621edbda8e2e67c624 100644 (file)
@@ -367,6 +367,39 @@ channel_still_useful(channel_T *channel)
            || (channel->ch_part[PART_ERR].ch_callback != NULL && has_err_msg);
 }
 
+/*
+ * 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
@@ -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.
  */
index 3dc3e210dedc27c837e811bdb4b4af98576516ed..1c5a31658bdd3801116fcdc8e05213069d73da70 100644 (file)
@@ -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);
 }
 
 /*
index 05dec4a0e38fb3a61c078a6f2d52a8e08825efef..3278ad977568d036ddd411b33b13a11348d1eb4f 100644 (file)
@@ -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
 
 /*
index b1910d95d06395ef79163927605937bd4147c86d..0584877eb2445cec494d238f4c3ffa3369e5a26f 100644 (file)
--- 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;
index e4ef00309ce01d97b911f7569847d3acef0f14e6..d5114cba42c883cb3343403bf08b98871cc36138 100644 (file)
@@ -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);
index 1de7a6d9dc83f0e9db213e6d61b1c03f72a2c6bd..38392b9bd2d48fa6c0ab2a8a25137588512007da 100644 (file)
@@ -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);
index b20e9c5ec564edd7e26fe1afc8f5b42d29007e71..fada9fe95f9ff92c16a3f9739ee961139351b438 100644 (file)
@@ -7910,7 +7910,7 @@ reg_submatch_list(int no)
 
     if (error)
     {
-       list_free(list, TRUE);
+       list_free(list);
        return NULL;
     }
     return list;
index e753860b82a3659020b9b32f0916adbff1542a70..adee3e807e31fe198fe42f2c9354c46d1ba68f6a 100644 (file)
@@ -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 */
index 154125950d485c28942bd936fd7848cf8e448a2f..4224c6fc259165d989f51005416b25a4e26595bb 100644 (file)
--- 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);
 
index 2d53e82078504b98dedce7a7b1395160f9aec0d3..404bb748db65aae559751389e2376f7096b789a3 100644 (file)
@@ -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
index a808bb727687fc52ccb0022dd833a6352b26c494..dd50f983d9922ac1425fc888f85445197eedd232 100644 (file)
@@ -748,6 +748,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1719,
 /**/
     1718,
 /**/