comparison src/userfunc.c @ 16003:879829e44091 v8.1.1007

patch 8.1.1007: using closure may consume a lot of memory commit https://github.com/vim/vim/commit/209b8e3e3bf7a4a3d102134124120f6c7f57d560 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Mar 14 13:43:24 2019 +0100 patch 8.1.1007: using closure may consume a lot of memory Problem: Using closure may consume a lot of memory. Solution: unreference items that are no longer needed. Add a test. (Ozaki Kiichi, closes #3961)
author Bram Moolenaar <Bram@vim.org>
date Thu, 14 Mar 2019 13:45:06 +0100
parents bd75c9df2a14
children 3d6b282e2d6e
comparison
equal deleted inserted replaced
16002:7834d7d14b48 16003:879829e44091
37 static hashtab_T func_hashtab; 37 static hashtab_T func_hashtab;
38 38
39 /* Used by get_func_tv() */ 39 /* Used by get_func_tv() */
40 static garray_T funcargs = GA_EMPTY; 40 static garray_T funcargs = GA_EMPTY;
41 41
42 /* pointer to funccal for currently active function */ 42 // pointer to funccal for currently active function
43 funccall_T *current_funccal = NULL; 43 static funccall_T *current_funccal = NULL;
44 44
45 /* Pointer to list of previously used funccal, still around because some 45 // Pointer to list of previously used funccal, still around because some
46 * item in it is still being used. */ 46 // item in it is still being used.
47 funccall_T *previous_funccal = NULL; 47 static funccall_T *previous_funccal = NULL;
48 48
49 static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it"); 49 static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it");
50 static char *e_funcdict = N_("E717: Dictionary entry already exists"); 50 static char *e_funcdict = N_("E717: Dictionary entry already exists");
51 static char *e_funcref = N_("E718: Funcref required"); 51 static char *e_funcref = N_("E718: Funcref required");
52 static char *e_nofunc = N_("E130: Unknown function: %s"); 52 static char *e_nofunc = N_("E130: Unknown function: %s");
584 v->di_tv.v_lock = VAR_FIXED; 584 v->di_tv.v_lock = VAR_FIXED;
585 v->di_tv.vval.v_number = nr; 585 v->di_tv.vval.v_number = nr;
586 } 586 }
587 587
588 /* 588 /*
589 * Free "fc" and what it contains. 589 * Free "fc".
590 */ 590 */
591 static void 591 static void
592 free_funccal( 592 free_funccal(funccall_T *fc)
593 funccall_T *fc, 593 {
594 int free_val) /* a: vars were allocated */ 594 int i;
595 {
596 listitem_T *li;
597 int i;
598 595
599 for (i = 0; i < fc->fc_funcs.ga_len; ++i) 596 for (i = 0; i < fc->fc_funcs.ga_len; ++i)
600 { 597 {
601 ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; 598 ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
602 599
603 /* When garbage collecting a funccall_T may be freed before the 600 // When garbage collecting a funccall_T may be freed before the
604 * function that references it, clear its uf_scoped field. 601 // function that references it, clear its uf_scoped field.
605 * The function may have been redefined and point to another 602 // The function may have been redefined and point to another
606 * funccall_T, don't clear it then. */ 603 // funccall_T, don't clear it then.
607 if (fp != NULL && fp->uf_scoped == fc) 604 if (fp != NULL && fp->uf_scoped == fc)
608 fp->uf_scoped = NULL; 605 fp->uf_scoped = NULL;
609 } 606 }
610 ga_clear(&fc->fc_funcs); 607 ga_clear(&fc->fc_funcs);
611 608
612 /* The a: variables typevals may not have been allocated, only free the
613 * allocated variables. */
614 vars_clear_ext(&fc->l_avars.dv_hashtab, free_val);
615
616 /* free all l: variables */
617 vars_clear(&fc->l_vars.dv_hashtab);
618
619 /* Free the a:000 variables if they were allocated. */
620 if (free_val)
621 for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
622 clear_tv(&li->li_tv);
623
624 func_ptr_unref(fc->func); 609 func_ptr_unref(fc->func);
625 vim_free(fc); 610 vim_free(fc);
626 } 611 }
627 612
628 /* 613 /*
614 * Free "fc" and what it contains.
615 * Can be called only when "fc" is kept beyond the period of it called,
616 * i.e. after cleanup_function_call(fc).
617 */
618 static void
619 free_funccal_contents(funccall_T *fc)
620 {
621 listitem_T *li;
622
623 // Free all l: variables.
624 vars_clear(&fc->l_vars.dv_hashtab);
625
626 // Free all a: variables.
627 vars_clear(&fc->l_avars.dv_hashtab);
628
629 // Free the a:000 variables.
630 for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
631 clear_tv(&li->li_tv);
632
633 free_funccal(fc);
634 }
635
636 /*
629 * Handle the last part of returning from a function: free the local hashtable. 637 * Handle the last part of returning from a function: free the local hashtable.
630 * Unless it is still in use by a closure. 638 * Unless it is still in use by a closure.
631 */ 639 */
632 static void 640 static void
633 cleanup_function_call(funccall_T *fc) 641 cleanup_function_call(funccall_T *fc)
634 { 642 {
643 int may_free_fc = fc->fc_refcount <= 0;
644 int free_fc = TRUE;
645
635 current_funccal = fc->caller; 646 current_funccal = fc->caller;
636 647
637 /* If the a:000 list and the l: and a: dicts are not referenced and there 648 // Free all l: variables if not referred.
638 * is no closure using it, we can free the funccall_T and what's in it. */ 649 if (may_free_fc && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT)
639 if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT 650 vars_clear(&fc->l_vars.dv_hashtab);
640 && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
641 && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
642 && fc->fc_refcount <= 0)
643 {
644 free_funccal(fc, FALSE);
645 }
646 else 651 else
647 { 652 free_fc = FALSE;
648 hashitem_T *hi; 653
649 listitem_T *li; 654 // If the a:000 list and the l: and a: dicts are not referenced and
650 int todo; 655 // there is no closure using it, we can free the funccall_T and what's
651 dictitem_T *v; 656 // in it.
652 static int made_copy = 0; 657 if (may_free_fc && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
653 658 vars_clear_ext(&fc->l_avars.dv_hashtab, FALSE);
654 /* "fc" is still in use. This can happen when returning "a:000", 659 else
655 * assigning "l:" to a global variable or defining a closure. 660 {
656 * Link "fc" in the list for garbage collection later. */ 661 int todo;
657 fc->caller = previous_funccal; 662 hashitem_T *hi;
658 previous_funccal = fc; 663 dictitem_T *di;
659 664
660 /* Make a copy of the a: variables, since we didn't do that above. */ 665 free_fc = FALSE;
666
667 // Make a copy of the a: variables, since we didn't do that above.
661 todo = (int)fc->l_avars.dv_hashtab.ht_used; 668 todo = (int)fc->l_avars.dv_hashtab.ht_used;
662 for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) 669 for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi)
663 { 670 {
664 if (!HASHITEM_EMPTY(hi)) 671 if (!HASHITEM_EMPTY(hi))
665 { 672 {
666 --todo; 673 --todo;
667 v = HI2DI(hi); 674 di = HI2DI(hi);
668 copy_tv(&v->di_tv, &v->di_tv); 675 copy_tv(&di->di_tv, &di->di_tv);
669 } 676 }
670 } 677 }
671 678 }
672 /* Make a copy of the a:000 items, since we didn't do that above. */ 679
680 if (may_free_fc && fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT)
681 fc->l_varlist.lv_first = NULL;
682 else
683 {
684 listitem_T *li;
685
686 free_fc = FALSE;
687
688 // Make a copy of the a:000 items, since we didn't do that above.
673 for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) 689 for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
674 copy_tv(&li->li_tv, &li->li_tv); 690 copy_tv(&li->li_tv, &li->li_tv);
675 691 }
676 if (++made_copy == 10000) 692
677 { 693 if (free_fc)
678 // We have made a lot of copies. This can happen when 694 free_funccal(fc);
679 // repetitively calling a function that creates a reference to 695 else
696 {
697 static int made_copy = 0;
698
699 // "fc" is still in use. This can happen when returning "a:000",
700 // assigning "l:" to a global variable or defining a closure.
701 // Link "fc" in the list for garbage collection later.
702 fc->caller = previous_funccal;
703 previous_funccal = fc;
704
705 if (want_garbage_collect)
706 // If garbage collector is ready, clear count.
707 made_copy = 0;
708 else if (++made_copy >= (int)((4096 * 1024) / sizeof(*fc)))
709 {
710 // We have made a lot of copies, worth 4 Mbyte. This can happen
711 // when repetitively calling a function that creates a reference to
680 // itself somehow. Call the garbage collector soon to avoid using 712 // itself somehow. Call the garbage collector soon to avoid using
681 // too much memory. 713 // too much memory.
682 made_copy = 0; 714 made_copy = 0;
683 want_garbage_collect = TRUE; 715 want_garbage_collect = TRUE;
684 } 716 }
729 } 761 }
730 ++depth; 762 ++depth;
731 763
732 line_breakcheck(); /* check for CTRL-C hit */ 764 line_breakcheck(); /* check for CTRL-C hit */
733 765
734 fc = (funccall_T *)alloc(sizeof(funccall_T)); 766 fc = (funccall_T *)alloc_clear(sizeof(funccall_T));
735 if (fc == NULL) 767 if (fc == NULL)
736 return; 768 return;
737 fc->caller = current_funccal; 769 fc->caller = current_funccal;
738 current_funccal = fc; 770 current_funccal = fc;
739 fc->func = fp; 771 fc->func = fp;
830 } 862 }
831 if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) 863 if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN)
832 { 864 {
833 v = &fc->fixvar[fixvar_idx++].var; 865 v = &fc->fixvar[fixvar_idx++].var;
834 v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; 866 v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
867 STRCPY(v->di_key, name);
835 } 868 }
836 else 869 else
837 { 870 {
838 v = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) 871 v = dictitem_alloc(name);
839 + STRLEN(name)));
840 if (v == NULL) 872 if (v == NULL)
841 break; 873 break;
842 v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; 874 v->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
843 } 875 }
844 STRCPY(v->di_key, name);
845 876
846 /* Note: the values are copied directly to avoid alloc/free. 877 /* Note: the values are copied directly to avoid alloc/free.
847 * "argvars" must have VAR_FIXED for v_lock. */ 878 * "argvars" must have VAR_FIXED for v_lock. */
848 v->di_tv = argvars[i]; 879 v->di_tv = argvars[i];
849 v->di_tv.v_lock = VAR_FIXED; 880 v->di_tv.v_lock = VAR_FIXED;
858 else 889 else
859 hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); 890 hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
860 891
861 if (ai >= 0 && ai < MAX_FUNC_ARGS) 892 if (ai >= 0 && ai < MAX_FUNC_ARGS)
862 { 893 {
863 list_append(&fc->l_varlist, &fc->l_listitems[ai]); 894 listitem_T *li = &fc->l_listitems[ai];
864 fc->l_listitems[ai].li_tv = argvars[i]; 895
865 fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; 896 li->li_tv = argvars[i];
897 li->li_tv.v_lock = VAR_FIXED;
898 list_append(&fc->l_varlist, li);
866 } 899 }
867 } 900 }
868 901
869 /* Don't redraw while executing the function. */ 902 /* Don't redraw while executing the function. */
870 ++RedrawingDisabled; 903 ++RedrawingDisabled;
1086 for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) 1119 for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller)
1087 { 1120 {
1088 if (fc == *pfc) 1121 if (fc == *pfc)
1089 { 1122 {
1090 *pfc = fc->caller; 1123 *pfc = fc->caller;
1091 free_funccal(fc, TRUE); 1124 free_funccal_contents(fc);
1092 return; 1125 return;
1093 } 1126 }
1094 } 1127 }
1095 for (i = 0; i < fc->fc_funcs.ga_len; ++i) 1128 for (i = 0; i < fc->fc_funcs.ga_len; ++i)
1096 if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) 1129 if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp)
3644 { 3677 {
3645 if (can_free_funccal(*pfc, copyID)) 3678 if (can_free_funccal(*pfc, copyID))
3646 { 3679 {
3647 fc = *pfc; 3680 fc = *pfc;
3648 *pfc = fc->caller; 3681 *pfc = fc->caller;
3649 free_funccal(fc, TRUE); 3682 free_funccal_contents(fc);
3650 did_free = TRUE; 3683 did_free = TRUE;
3651 did_free_funccal = TRUE; 3684 did_free_funccal = TRUE;
3652 } 3685 }
3653 else 3686 else
3654 pfc = &(*pfc)->caller; 3687 pfc = &(*pfc)->caller;