To: vim_dev@googlegroups.com Subject: Patch 7.4.2332 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.2332 Problem: Crash when stop_timer() is called in a callback of a callback. Vim hangs when the timer callback uses too much time. Solution: Set tr_id to -1 when a timer is to be deleted. Don't keep calling callbacks forever. (Ozaki Kiichi) Files: src/evalfunc.c, src/ex_cmds2.c, src/structs.h, src/proto/ex_cmds2.pro, src/testdir/test_timers.vim *** ../vim-7.4.2331/src/evalfunc.c 2016-09-04 21:42:32.410056052 +0200 --- src/evalfunc.c 2016-09-05 22:32:11.684576179 +0200 *************** *** 12398,12409 **** static void f_timer_start(typval_T *argvars, typval_T *rettv) { ! long msec = (long)get_tv_number(&argvars[0]); ! timer_T *timer; ! int repeat = 0; ! char_u *callback; ! dict_T *dict; if (check_secure()) return; if (argvars[2].v_type != VAR_UNKNOWN) --- 12398,12411 ---- static void f_timer_start(typval_T *argvars, typval_T *rettv) { ! long msec = (long)get_tv_number(&argvars[0]); ! timer_T *timer; ! int repeat = 0; ! char_u *callback; ! dict_T *dict; ! partial_T *partial; + rettv->vval.v_number = -1; if (check_secure()) return; if (argvars[2].v_type != VAR_UNKNOWN) *************** *** 12418,12430 **** repeat = get_dict_number(dict, (char_u *)"repeat"); } ! timer = create_timer(msec, repeat); ! callback = get_callback(&argvars[1], &timer->tr_partial); if (callback == NULL) ! { ! stop_timer(timer); ! rettv->vval.v_number = -1; ! } else { if (timer->tr_partial == NULL) --- 12420,12432 ---- repeat = get_dict_number(dict, (char_u *)"repeat"); } ! callback = get_callback(&argvars[1], &partial); if (callback == NULL) ! return; ! ! timer = create_timer(msec, repeat); ! if (timer == NULL) ! free_callback(callback, partial); else { if (timer->tr_partial == NULL) *************** *** 12432,12438 **** else /* pointer into the partial */ timer->tr_callback = callback; ! rettv->vval.v_number = timer->tr_id; } } --- 12434,12441 ---- else /* pointer into the partial */ timer->tr_callback = callback; ! timer->tr_partial = partial; ! rettv->vval.v_number = (varnumber_T)timer->tr_id; } } *** ../vim-7.4.2331/src/ex_cmds2.c 2016-09-02 22:18:45.458101130 +0200 --- src/ex_cmds2.c 2016-09-05 22:33:24.691896430 +0200 *************** *** 1088,1097 **** # if defined(FEAT_TIMERS) || defined(PROTO) static timer_T *first_timer = NULL; ! static int last_timer_id = 0; ! static timer_T *current_timer = NULL; ! static int free_current_timer = FALSE; /* * Insert a timer in the list of timers. --- 1088,1104 ---- # if defined(FEAT_TIMERS) || defined(PROTO) static timer_T *first_timer = NULL; ! static long last_timer_id = 0; ! # ifdef WIN3264 ! # define GET_TIMEDIFF(timer, now) \ ! (long)(((double)(timer->tr_due.QuadPart - now.QuadPart) \ ! / (double)fr.QuadPart) * 1000); ! # else ! # define GET_TIMEDIFF(timer, now) \ ! (timer->tr_due.tv_sec - now.tv_sec) * 1000 \ ! + (timer->tr_due.tv_usec - now.tv_usec) / 1000; ! # endif /* * Insert a timer in the list of timers. *************** *** 1124,1136 **** static void free_timer(timer_T *timer) { ! if (timer == current_timer) ! free_current_timer = TRUE; ! else ! { ! free_callback(timer->tr_callback, timer->tr_partial); ! vim_free(timer); ! } } /* --- 1131,1138 ---- static void free_timer(timer_T *timer) { ! free_callback(timer->tr_callback, timer->tr_partial); ! vim_free(timer); } /* *************** *** 1144,1150 **** if (timer == NULL) return NULL; ! timer->tr_id = ++last_timer_id; insert_timer(timer); if (repeat != 0) timer->tr_repeat = repeat - 1; --- 1146,1155 ---- if (timer == NULL) return NULL; ! if (++last_timer_id < 0) ! /* Overflow! Might cause duplicates... */ ! last_timer_id = 0; ! timer->tr_id = last_timer_id; insert_timer(timer); if (repeat != 0) timer->tr_repeat = repeat - 1; *************** *** 1165,1171 **** typval_T argv[2]; argv[0].v_type = VAR_NUMBER; ! argv[0].vval.v_number = timer->tr_id; argv[1].v_type = VAR_UNKNOWN; call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback), --- 1170,1176 ---- typval_T argv[2]; argv[0].v_type = VAR_NUMBER; ! argv[0].vval.v_number = (varnumber_T)timer->tr_id; argv[1].v_type = VAR_UNKNOWN; call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback), *************** *** 1182,1258 **** check_due_timer(void) { timer_T *timer; long this_due; long next_due = -1; proftime_T now; int did_one = FALSE; # ifdef WIN3264 LARGE_INTEGER fr; QueryPerformanceFrequency(&fr); # endif ! while (!got_int) { ! profile_start(&now); ! next_due = -1; ! for (timer = first_timer; timer != NULL; timer = timer->tr_next) { ! if (timer->tr_paused) ! continue; ! # ifdef WIN3264 ! this_due = (long)(((double)(timer->tr_due.QuadPart - now.QuadPart) ! / (double)fr.QuadPart) * 1000); ! # else ! this_due = (timer->tr_due.tv_sec - now.tv_sec) * 1000 ! + (timer->tr_due.tv_usec - now.tv_usec) / 1000; ! # endif ! if (this_due <= 1) { ! current_timer = timer; ! free_current_timer = FALSE; ! timer_callback(timer); ! current_timer = NULL; ! ! did_one = TRUE; ! if (timer->tr_repeat != 0 && !free_current_timer) ! { ! profile_setlimit(timer->tr_interval, &timer->tr_due); ! if (timer->tr_repeat > 0) ! --timer->tr_repeat; ! } ! else ! { ! remove_timer(timer); ! free_timer(timer); ! } ! /* the callback may do anything, start all over */ ! break; } - if (next_due == -1 || next_due > this_due) - next_due = this_due; } ! if (timer == NULL) ! break; } if (did_one) redraw_after_callback(); ! return next_due; } /* * Find a timer by ID. Returns NULL if not found; */ timer_T * ! find_timer(int id) { timer_T *timer; ! for (timer = first_timer; timer != NULL; timer = timer->tr_next) ! if (timer->tr_id == id) ! break; ! return timer; } --- 1187,1262 ---- check_due_timer(void) { timer_T *timer; + timer_T *timer_next; long this_due; long next_due = -1; proftime_T now; int did_one = FALSE; + long current_id = last_timer_id; # ifdef WIN3264 LARGE_INTEGER fr; QueryPerformanceFrequency(&fr); # endif ! profile_start(&now); ! for (timer = first_timer; timer != NULL && !got_int; timer = timer_next) { ! timer_next = timer->tr_next; ! ! if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused) ! continue; ! this_due = GET_TIMEDIFF(timer, now); ! if (this_due <= 1) { ! timer->tr_firing = TRUE; ! timer_callback(timer); ! timer->tr_firing = FALSE; ! timer_next = timer->tr_next; ! did_one = TRUE; ! ! /* Only fire the timer again if it repeats and stop_timer() wasn't ! * called while inside the callback (tr_id == -1). */ ! if (timer->tr_repeat != 0 && timer->tr_id != -1) ! { ! profile_setlimit(timer->tr_interval, &timer->tr_due); ! this_due = GET_TIMEDIFF(timer, now); ! if (this_due < 1) ! this_due = 1; ! if (timer->tr_repeat > 0) ! --timer->tr_repeat; ! } ! else { ! this_due = -1; ! remove_timer(timer); ! free_timer(timer); } } ! if (this_due > 0 && (next_due == -1 || next_due > this_due)) ! next_due = this_due; } if (did_one) redraw_after_callback(); ! return current_id != last_timer_id ? 1 : next_due; } /* * Find a timer by ID. Returns NULL if not found; */ timer_T * ! find_timer(long id) { timer_T *timer; ! if (id >= 0) ! { ! for (timer = first_timer; timer != NULL; timer = timer->tr_next) ! if (timer->tr_id == id) ! return timer; ! } ! return NULL; } *************** *** 1262,1276 **** void stop_timer(timer_T *timer) { ! remove_timer(timer); ! free_timer(timer); } void stop_all_timers(void) { ! while (first_timer != NULL) ! stop_timer(first_timer); } void --- 1266,1292 ---- void stop_timer(timer_T *timer) { ! if (timer->tr_firing) ! /* Free the timer after the callback returns. */ ! timer->tr_id = -1; ! else ! { ! remove_timer(timer); ! free_timer(timer); ! } } void stop_all_timers(void) { ! timer_T *timer; ! timer_T *timer_next; ! ! for (timer = first_timer; timer != NULL; timer = timer_next) ! { ! timer_next = timer->tr_next; ! stop_timer(timer); ! } } void *************** *** 1289,1306 **** return; list_append_dict(list, dict); ! dict_add_nr_str(dict, "id", (long)timer->tr_id, NULL); dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL); profile_start(&now); # ifdef WIN3264 QueryPerformanceFrequency(&fr); - remaining = (long)(((double)(timer->tr_due.QuadPart - now.QuadPart) - / (double)fr.QuadPart) * 1000); - # else - remaining = (timer->tr_due.tv_sec - now.tv_sec) * 1000 - + (timer->tr_due.tv_usec - now.tv_usec) / 1000; # endif dict_add_nr_str(dict, "remaining", (long)remaining, NULL); dict_add_nr_str(dict, "repeat", --- 1305,1318 ---- return; list_append_dict(list, dict); ! dict_add_nr_str(dict, "id", timer->tr_id, NULL); dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL); profile_start(&now); # ifdef WIN3264 QueryPerformanceFrequency(&fr); # endif + remaining = GET_TIMEDIFF(timer, now); dict_add_nr_str(dict, "remaining", (long)remaining, NULL); dict_add_nr_str(dict, "repeat", *************** *** 1333,1339 **** timer_T *timer; for (timer = first_timer; timer != NULL; timer = timer->tr_next) ! add_timer_info(rettv, timer); } /* --- 1345,1352 ---- timer_T *timer; for (timer = first_timer; timer != NULL; timer = timer->tr_next) ! if (timer->tr_id != -1) ! add_timer_info(rettv, timer); } /* *** ../vim-7.4.2331/src/structs.h 2016-09-04 19:50:50.512985798 +0200 --- src/structs.h 2016-09-05 22:31:12.977124504 +0200 *************** *** 3166,3177 **** typedef struct timer_S timer_T; struct timer_S { ! int tr_id; #ifdef FEAT_TIMERS timer_T *tr_next; timer_T *tr_prev; proftime_T tr_due; /* when the callback is to be invoked */ ! int tr_paused; /* when TRUE callback is not invoked */ int tr_repeat; /* number of times to repeat, -1 forever */ long tr_interval; /* msec */ char_u *tr_callback; /* allocated */ --- 3166,3178 ---- typedef struct timer_S timer_T; struct timer_S { ! long tr_id; #ifdef FEAT_TIMERS timer_T *tr_next; timer_T *tr_prev; proftime_T tr_due; /* when the callback is to be invoked */ ! char tr_firing; /* when TRUE callback is being called */ ! char tr_paused; /* when TRUE callback is not invoked */ int tr_repeat; /* number of times to repeat, -1 forever */ long tr_interval; /* msec */ char_u *tr_callback; /* allocated */ *** ../vim-7.4.2331/src/proto/ex_cmds2.pro 2016-08-07 18:22:30.426091047 +0200 --- src/proto/ex_cmds2.pro 2016-09-05 22:33:47.167687629 +0200 *************** *** 20,26 **** void profile_zero(proftime_T *tm); timer_T *create_timer(long msec, int repeat); long check_due_timer(void); ! timer_T *find_timer(int id); void stop_timer(timer_T *timer); void stop_all_timers(void); void add_timer_info(typval_T *rettv, timer_T *timer); --- 20,26 ---- void profile_zero(proftime_T *tm); timer_T *create_timer(long msec, int repeat); long check_due_timer(void); ! timer_T *find_timer(long id); void stop_timer(timer_T *timer); void stop_all_timers(void); void add_timer_info(typval_T *rettv, timer_T *timer); *** ../vim-7.4.2331/src/testdir/test_timers.vim 2016-09-01 21:26:16.166308476 +0200 --- src/testdir/test_timers.vim 2016-09-05 22:42:17.966533913 +0200 *************** *** 143,146 **** --- 143,176 ---- call assert_equal([], timer_info(t)) endfunc + func StopTimer1(timer) + let g:timer2 = timer_start(10, 'StopTimer2') + " avoid maxfuncdepth error + call timer_pause(g:timer1, 1) + sleep 40m + endfunc + + func StopTimer2(timer) + call timer_stop(g:timer1) + endfunc + + func Test_stop_in_callback() + let g:timer1 = timer_start(10, 'StopTimer1') + sleep 40m + endfunc + + func StopTimerAll(timer) + call timer_stopall() + endfunc + + func Test_stop_all_in_callback() + let g:timer1 = timer_start(10, 'StopTimerAll') + let info = timer_info() + call assert_equal(1, len(info)) + sleep 40m + let info = timer_info() + call assert_equal(0, len(info)) + endfunc + + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-7.4.2331/src/version.c 2016-09-05 21:51:10.271270426 +0200 --- src/version.c 2016-09-05 22:07:31.470505700 +0200 *************** *** 765,766 **** --- 765,768 ---- { /* Add new patch number below this line */ + /**/ + 2332, /**/ -- hundred-and-one symptoms of being an internet addict: 175. You send yourself e-mail before you go to bed to remind you what to do when you wake up. /// Bram Moolenaar -- Bram@Moolenaar.net -- 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 ///