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 -- [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.