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.
Files:      src/eval.c, src/channel.c, src/globals.h, src/ops.c,
            src/regexp.c, src/tag.c, src/proto/channel.pro,
            src/proto/eval.pro, src/testdir/test_partial.vim,
            src/structs.h


*** ../vim-7.4.1718/src/eval.c  2016-04-07 22:16:26.484303131 +0200
--- src/eval.c  2016-04-08 16:03:27.602304722 +0200
***************
*** 430,435 ****
--- 430,437 ----
  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,464 ****
--- 461,469 ----
  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,5595 ****
                    {
                        if (list_append_tv(l, &item->li_tv) == FAIL)
                        {
!                           list_free(l, TRUE);
                            return FAIL;
                        }
                        item = item->li_next;
--- 5594,5600 ----
                    {
                        if (list_append_tv(l, &item->li_tv) == FAIL)
                        {
!                           list_free(l);
                            return FAIL;
                        }
                        item = item->li_next;
***************
*** 5930,5949 ****
  }
  
      static void
! partial_free(partial_T *pt, int recursive)
  {
      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);
!     }
      vim_free(pt->pt_argv);
!     if (recursive)
!       dict_unref(pt->pt_dict);
      func_unref(pt->pt_name);
      vim_free(pt->pt_name);
      vim_free(pt);
--- 5935,5948 ----
  }
  
      static void
! partial_free(partial_T *pt)
  {
      int i;
  
      for (i = 0; i < pt->pt_argc; ++i)
!       clear_tv(&pt->pt_argv[i]);
      vim_free(pt->pt_argv);
!     dict_unref(pt->pt_dict);
      func_unref(pt->pt_name);
      vim_free(pt->pt_name);
      vim_free(pt);
***************
*** 5957,5983 ****
  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);
  }
  
  /*
--- 5956,5962 ----
  partial_unref(partial_T *pt)
  {
      if (pt != NULL && --pt->pt_refcount <= 0)
!       partial_free(pt);
  }
  
  /*
***************
*** 6031,6037 ****
        EMSG2(_("E697: Missing end of List ']': %s"), *arg);
  failret:
        if (evaluate)
!           list_free(l, TRUE);
        return FAIL;
      }
  
--- 6010,6016 ----
        EMSG2(_("E697: Missing end of List ']': %s"), *arg);
  failret:
        if (evaluate)
!           list_free(l);
        return FAIL;
      }
  
***************
*** 6095,6114 ****
  list_unref(list_T *l)
  {
      if (l != NULL && --l->lv_refcount <= 0)
!       list_free(l, TRUE);
  }
  
  /*
   * 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. */
  {
      listitem_T *item;
  
      /* Remove the list from the list of lists for garbage collection. */
      if (l->lv_used_prev == NULL)
        first_list = l->lv_used_next;
--- 6074,6103 ----
  list_unref(list_T *l)
  {
      if (l != NULL && --l->lv_refcount <= 0)
!       list_free(l);
  }
  
  /*
   * Free a list, including all non-container items it points to.
   * Ignores the reference count.
   */
!     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,6133 ****
      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)
      {
!       /* 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);
      }
-     vim_free(l);
  }
  
  /*
--- 6106,6122 ----
      if (l->lv_used_next != NULL)
        l->lv_used_next->lv_used_prev = l->lv_used_prev;
  
!     vim_free(l);
! }
! 
!     void
! list_free(list_T *l)
! {
!     if (!in_free_unref_items)
      {
!       list_free_contents(l);
!       list_free_list(l);
      }
  }
  
  /*
***************
*** 7016,7022 ****
  #endif
  
  #ifdef FEAT_JOB_CHANNEL
!     abort = abort || set_ref_in_channel(copyID);
  #endif
  
      if (!abort)
--- 7005,7011 ----
  #endif
  
  #ifdef FEAT_JOB_CHANNEL
! //    abort = abort || set_ref_in_channel(copyID);
  #endif
  
      if (!abort)
***************
*** 7056,7062 ****
  }
  
  /*
!  * Free lists, dictionaries and jobs that are no longer referenced.
   */
      static int
  free_unref_items(int copyID)
--- 7045,7051 ----
  }
  
  /*
!  * Free lists, dictionaries, channels and jobs that are no longer referenced.
   */
      static int
  free_unref_items(int copyID)
***************
*** 7065,7093 ****
      list_T    *ll, *ll_next;
      int               did_free = FALSE;
  
      /*
       * Go through the list of dicts and free items without the copyID.
       */
!     for (dd = first_dict; dd != NULL; )
!     {
!       dd_next = 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);
            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; )
      {
        ll_next = ll->lv_used_next;
        if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
--- 7054,7119 ----
      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 = 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_contents(dd);
            did_free = TRUE;
        }
  
      /*
       * 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; 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,7107 ****
            /* 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;
        }
-       ll = ll_next;
      }
  
      return did_free;
  }
  
--- 7122,7143 ----
            /* 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_list(ll);
        }
      }
  
+ #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,7221 ****
      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)
!           dd = tv->vval.v_dict;
!       else if (tv->vval.v_partial != NULL)
!           dd = tv->vval.v_partial->pt_dict;
!       else
!           dd = NULL;
        if (dd != NULL && dd->dv_copyID != copyID)
        {
            /* Didn't see this dict yet. */
--- 7240,7251 ----
      ht_stack_T            **ht_stack,
      list_stack_T    **list_stack)
  {
      int               abort = FALSE;
  
!     if (tv->v_type == VAR_DICT)
      {
!       dict_T  *dd = tv->vval.v_dict;
! 
        if (dd != NULL && dd->dv_copyID != copyID)
        {
            /* Didn't see this dict yet. */
***************
*** 7237,7256 ****
                }
            }
        }
-       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;
        if (ll != NULL && ll->lv_copyID != copyID)
        {
            /* Didn't see this list yet. */
--- 7267,7277 ----
                }
            }
        }
      }
      else if (tv->v_type == VAR_LIST)
      {
!       list_T  *ll = tv->vval.v_list;
! 
        if (ll != NULL && ll->lv_copyID != copyID)
        {
            /* Didn't see this list yet. */
***************
*** 7274,7279 ****
--- 7295,7390 ----
            }
        }
      }
+     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,7361 ****
  dict_unref(dict_T *d)
  {
      if (d != NULL && --d->dv_refcount <= 0)
!       dict_free(d, TRUE);
  }
  
  /*
   * 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. */
  {
      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;
--- 7443,7462 ----
  dict_unref(dict_T *d)
  {
      if (d != NULL && --d->dv_refcount <= 0)
!       dict_free(d);
  }
  
  /*
   * Free a Dictionary, including all non-container items it contains.
   * Ignores the reference count.
   */
!     static void
! dict_free_contents(dict_T *d)
  {
      int               todo;
      hashitem_T        *hi;
      dictitem_T        *di;
  
      /* 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,7384 ****
             * 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);
            vim_free(di);
            --todo;
        }
      }
      hash_clear(&d->dv_hashtab);
      vim_free(d);
  }
  
  /*
   * Allocate a Dictionary item.
   * The "key" is copied to the new item.
--- 7468,7504 ----
             * something recursive causing trouble. */
            di = HI2DI(hi);
            hash_remove(&d->dv_hashtab, hi);
!           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,7833 ****
        EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
  failret:
        if (evaluate)
!           dict_free(d, TRUE);
        return FAIL;
      }
  
--- 7947,7953 ----
        EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
  failret:
        if (evaluate)
!           dict_free(d);
        return FAIL;
      }
  
***************
*** 7842,7850 ****
      return OK;
  }
  
- #if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
- #endif
- 
      static char *
  get_var_special_name(int nr)
  {
--- 7962,7967 ----
***************
*** 15391,15397 ****
                    || list_append_number(rettv->vval.v_list,
                                            (varnumber_T)-1) == FAIL))
        {
!               list_free(rettv->vval.v_list, TRUE);
                rettv->vval.v_list = NULL;
                goto theend;
        }
--- 15508,15514 ----
                    || list_append_number(rettv->vval.v_list,
                                            (varnumber_T)-1) == FAIL))
        {
!               list_free(rettv->vval.v_list);
                rettv->vval.v_list = NULL;
                goto theend;
        }
***************
*** 16488,16494 ****
  
      if (failed)
      {
!       list_free(rettv->vval.v_list, TRUE);
        /* readfile doc says an empty list is returned on error */
        rettv->vval.v_list = list_alloc();
      }
--- 16605,16611 ----
  
      if (failed)
      {
!       list_free(rettv->vval.v_list);
        /* readfile doc says an empty list is returned on error */
        rettv->vval.v_list = list_alloc();
      }
***************
*** 20070,20076 ****
      if (res != NULL)
        vim_free(res);
      if (list != NULL)
!       list_free(list, TRUE);
  }
  
  /*
--- 20187,20193 ----
      if (res != NULL)
        vim_free(res);
      if (list != NULL)
!       list_free(list);
  }
  
  /*
*** ../vim-7.4.1718/src/channel.c       2016-04-07 21:40:34.066966030 +0200
--- src/channel.c       2016-04-08 16:08:07.127390181 +0200
***************
*** 368,373 ****
--- 368,406 ----
  }
  
  /*
+  * 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,418 ****
      return FALSE;
  }
  
! /*
!  * Close a channel and free all its resources.
!  */
      void
! channel_free(channel_T *channel)
  {
!     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);
  }
  
  #if defined(FEAT_GUI) || defined(PROTO)
--- 430,468 ----
      return FALSE;
  }
  
!     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
! free_unused_channels(int copyID, int mask)
  {
!     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,2462 ****
--- 2507,2513 ----
      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,3919 ****
  static job_T *first_job = NULL;
  
      static void
! job_free(job_T *job)
  {
      ch_log(job->jv_channel, "Freeing job");
      if (job->jv_channel != NULL)
--- 3964,3970 ----
  static job_T *first_job = NULL;
  
      static void
! job_free_contents(job_T *job)
  {
      ch_log(job->jv_channel, "Freeing job");
      if (job->jv_channel != NULL)
***************
*** 3928,3946 ****
      }
      mch_clear_job(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);
  }
  
      void
  job_unref(job_T *job)
  {
--- 3979,4011 ----
      }
      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);
  }
  
+     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)
  {
***************
*** 3963,3968 ****
--- 4028,4068 ----
        }
      }
  }
+ 
+     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.
*** ../vim-7.4.1718/src/globals.h       2016-03-19 22:11:47.436674835 +0100
--- src/globals.h       2016-04-08 15:18:00.510711678 +0200
***************
*** 1619,1624 ****
--- 1619,1626 ----
  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
  
  /*
*** ../vim-7.4.1718/src/ops.c   2016-03-21 23:13:28.432710474 +0100
--- src/ops.c   2016-04-08 15:20:21.385237139 +0200
***************
*** 6391,6397 ****
        {
            if (list_append_string(list, NULL, -1) == FAIL)
            {
!               list_free(list, TRUE);
                return NULL;
            }
            list->lv_first->li_tv.vval.v_string = s;
--- 6391,6397 ----
        {
            if (list_append_string(list, NULL, -1) == FAIL)
            {
!               list_free(list);
                return NULL;
            }
            list->lv_first->li_tv.vval.v_string = s;
***************
*** 6465,6471 ****
                error = TRUE;
        if (error)
        {
!           list_free(list, TRUE);
            return NULL;
        }
        return (char_u *)list;
--- 6465,6471 ----
                error = TRUE;
        if (error)
        {
!           list_free(list);
            return NULL;
        }
        return (char_u *)list;
*** ../vim-7.4.1718/src/regexp.c        2016-04-03 14:00:29.320148959 +0200
--- src/regexp.c        2016-04-08 15:20:25.981189037 +0200
***************
*** 7910,7916 ****
  
      if (error)
      {
!       list_free(list, TRUE);
        return NULL;
      }
      return list;
--- 7910,7916 ----
  
      if (error)
      {
!       list_free(list);
        return NULL;
      }
      return list;
*** ../vim-7.4.1718/src/tag.c   2016-03-12 22:11:34.243300238 +0100
--- src/tag.c   2016-04-08 15:20:36.077083375 +0200
***************
*** 792,798 ****
                    vim_free(cmd);
                    vim_free(fname);
                    if (list != NULL)
!                       list_free(list, TRUE);
                    goto end_do_tag;
                }
  
--- 792,798 ----
                    vim_free(cmd);
                    vim_free(fname);
                    if (list != NULL)
!                       list_free(list);
                    goto end_do_tag;
                }
  
***************
*** 919,925 ****
                vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
                set_errorlist(curwin, list, ' ', IObuff);
  
!               list_free(list, TRUE);
                vim_free(fname);
                vim_free(cmd);
  
--- 919,925 ----
                vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
                set_errorlist(curwin, list, ' ', IObuff);
  
!               list_free(list);
                vim_free(fname);
                vim_free(cmd);
  
*** ../vim-7.4.1718/src/proto/channel.pro       2016-04-07 21:40:34.066966030 
+0200
--- src/proto/channel.pro       2016-04-08 16:08:10.363356443 +0200
***************
*** 5,11 ****
  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);
  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);
--- 5,12 ----
  void ch_logs(channel_T *ch, char *msg, char *name);
  channel_T *add_channel(void);
  int channel_unref(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,55 ****
--- 51,58 ----
  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);
*** ../vim-7.4.1718/src/proto/eval.pro  2016-03-15 23:10:26.412712095 +0100
--- src/proto/eval.pro  2016-04-08 15:28:53.151882878 +0200
***************
*** 45,54 ****
  int do_unlet(char_u *name, int forceit);
  void del_menutrans_vars(void);
  char_u *get_user_var_name(expand_T *xp, int idx);
  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);
  listitem_T *listitem_alloc(void);
  void listitem_free(listitem_T *item);
  void listitem_remove(list_T *l, listitem_T *item);
--- 45,56 ----
  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_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,77 ****
  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);
  dictitem_T *dictitem_alloc(char_u *key);
  void dictitem_free(dictitem_T *item);
  int dict_add(dict_T *d, dictitem_T *item);
--- 73,80 ----
  dict_T *dict_alloc(void);
  int rettv_dict_alloc(typval_T *rettv);
  void dict_unref(dict_T *d);
! 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,93 ****
  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);
--- 90,95 ----
*** ../vim-7.4.1718/src/testdir/test_partial.vim        2016-04-06 
22:59:33.503226978 +0200
--- src/testdir/test_partial.vim        2016-04-08 16:44:37.140414326 +0200
***************
*** 221,227 ****
    endif
  endfunc
  
! " This causes double free on exit if EXITFREE is defined.
  func Test_cyclic_list_arg()
    let l = []
    let Pt = function('string', [l])
--- 221,227 ----
    endif
  endfunc
  
! " This caused double free on exit if EXITFREE is defined.
  func Test_cyclic_list_arg()
    let l = []
    let Pt = function('string', [l])
***************
*** 230,236 ****
    unlet Pt
  endfunc
  
! " This causes double free on exit if EXITFREE is defined.
  func Test_cyclic_dict_arg()
    let d = {}
    let Pt = function('string', [d])
--- 230,236 ----
    unlet Pt
  endfunc
  
! " This caused double free on exit if EXITFREE is defined.
  func Test_cyclic_dict_arg()
    let d = {}
    let Pt = function('string', [d])
***************
*** 238,240 ****
--- 238,255 ----
    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
*** ../vim-7.4.1718/src/structs.h       2016-03-28 19:16:15.669846492 +0200
--- src/structs.h       2016-04-08 13:16:05.658512920 +0200
***************
*** 1290,1295 ****
--- 1290,1297 ----
      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,1435 ****
  
      job_T     *ch_job;        /* Job that uses this channel; this does not
                                 * count as a reference to avoid a circular
!                                * reference. */
      int               ch_job_killed;  /* TRUE when there was a job and it was 
killed
                                 * or we know it died. */
  
      int               ch_refcount;    /* reference count */
  };
  
  #define JO_MODE                   0x0001      /* channel mode */
--- 1427,1438 ----
  
      job_T     *ch_job;        /* Job that uses this channel; this does not
                                 * count as a reference to avoid a circular
!                                * 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 */
*** ../vim-7.4.1718/src/version.c       2016-04-07 22:16:26.484303131 +0200
--- src/version.c       2016-04-08 16:10:03.498177031 +0200
***************
*** 750,751 ****
--- 750,753 ----
  {   /* Add new patch number below this line */
+ /**/
+     1719,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
246. You use up your free 1 Gbyte in two days.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui