mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
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)
This commit is contained in:
@@ -12403,7 +12403,9 @@ f_timer_start(typval_T *argvars, typval_T *rettv)
|
|||||||
int repeat = 0;
|
int repeat = 0;
|
||||||
char_u *callback;
|
char_u *callback;
|
||||||
dict_T *dict;
|
dict_T *dict;
|
||||||
|
partial_T *partial;
|
||||||
|
|
||||||
|
rettv->vval.v_number = -1;
|
||||||
if (check_secure())
|
if (check_secure())
|
||||||
return;
|
return;
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||||
@@ -12418,13 +12420,13 @@ f_timer_start(typval_T *argvars, typval_T *rettv)
|
|||||||
repeat = get_dict_number(dict, (char_u *)"repeat");
|
repeat = get_dict_number(dict, (char_u *)"repeat");
|
||||||
}
|
}
|
||||||
|
|
||||||
timer = create_timer(msec, repeat);
|
callback = get_callback(&argvars[1], &partial);
|
||||||
callback = get_callback(&argvars[1], &timer->tr_partial);
|
|
||||||
if (callback == NULL)
|
if (callback == NULL)
|
||||||
{
|
return;
|
||||||
stop_timer(timer);
|
|
||||||
rettv->vval.v_number = -1;
|
timer = create_timer(msec, repeat);
|
||||||
}
|
if (timer == NULL)
|
||||||
|
free_callback(callback, partial);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (timer->tr_partial == NULL)
|
if (timer->tr_partial == NULL)
|
||||||
@@ -12432,7 +12434,8 @@ f_timer_start(typval_T *argvars, typval_T *rettv)
|
|||||||
else
|
else
|
||||||
/* pointer into the partial */
|
/* pointer into the partial */
|
||||||
timer->tr_callback = callback;
|
timer->tr_callback = callback;
|
||||||
rettv->vval.v_number = timer->tr_id;
|
timer->tr_partial = partial;
|
||||||
|
rettv->vval.v_number = (varnumber_T)timer->tr_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
101
src/ex_cmds2.c
101
src/ex_cmds2.c
@@ -1088,10 +1088,17 @@ profile_zero(proftime_T *tm)
|
|||||||
|
|
||||||
# if defined(FEAT_TIMERS) || defined(PROTO)
|
# if defined(FEAT_TIMERS) || defined(PROTO)
|
||||||
static timer_T *first_timer = NULL;
|
static timer_T *first_timer = NULL;
|
||||||
static int last_timer_id = 0;
|
static long last_timer_id = 0;
|
||||||
|
|
||||||
static timer_T *current_timer = NULL;
|
# ifdef WIN3264
|
||||||
static int free_current_timer = FALSE;
|
# 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.
|
* Insert a timer in the list of timers.
|
||||||
@@ -1123,15 +1130,10 @@ remove_timer(timer_T *timer)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
free_timer(timer_T *timer)
|
free_timer(timer_T *timer)
|
||||||
{
|
|
||||||
if (timer == current_timer)
|
|
||||||
free_current_timer = TRUE;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
free_callback(timer->tr_callback, timer->tr_partial);
|
free_callback(timer->tr_callback, timer->tr_partial);
|
||||||
vim_free(timer);
|
vim_free(timer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a timer and return it. NULL if out of memory.
|
* Create a timer and return it. NULL if out of memory.
|
||||||
@@ -1144,7 +1146,10 @@ create_timer(long msec, int repeat)
|
|||||||
|
|
||||||
if (timer == NULL)
|
if (timer == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
timer->tr_id = ++last_timer_id;
|
if (++last_timer_id < 0)
|
||||||
|
/* Overflow! Might cause duplicates... */
|
||||||
|
last_timer_id = 0;
|
||||||
|
timer->tr_id = last_timer_id;
|
||||||
insert_timer(timer);
|
insert_timer(timer);
|
||||||
if (repeat != 0)
|
if (repeat != 0)
|
||||||
timer->tr_repeat = repeat - 1;
|
timer->tr_repeat = repeat - 1;
|
||||||
@@ -1165,7 +1170,7 @@ timer_callback(timer_T *timer)
|
|||||||
typval_T argv[2];
|
typval_T argv[2];
|
||||||
|
|
||||||
argv[0].v_type = VAR_NUMBER;
|
argv[0].v_type = VAR_NUMBER;
|
||||||
argv[0].vval.v_number = timer->tr_id;
|
argv[0].vval.v_number = (varnumber_T)timer->tr_id;
|
||||||
argv[1].v_type = VAR_UNKNOWN;
|
argv[1].v_type = VAR_UNKNOWN;
|
||||||
|
|
||||||
call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
|
call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
|
||||||
@@ -1182,78 +1187,77 @@ timer_callback(timer_T *timer)
|
|||||||
check_due_timer(void)
|
check_due_timer(void)
|
||||||
{
|
{
|
||||||
timer_T *timer;
|
timer_T *timer;
|
||||||
|
timer_T *timer_next;
|
||||||
long this_due;
|
long this_due;
|
||||||
long next_due = -1;
|
long next_due = -1;
|
||||||
proftime_T now;
|
proftime_T now;
|
||||||
int did_one = FALSE;
|
int did_one = FALSE;
|
||||||
|
long current_id = last_timer_id;
|
||||||
# ifdef WIN3264
|
# ifdef WIN3264
|
||||||
LARGE_INTEGER fr;
|
LARGE_INTEGER fr;
|
||||||
|
|
||||||
QueryPerformanceFrequency(&fr);
|
QueryPerformanceFrequency(&fr);
|
||||||
# endif
|
# endif
|
||||||
while (!got_int)
|
|
||||||
{
|
|
||||||
profile_start(&now);
|
profile_start(&now);
|
||||||
next_due = -1;
|
for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
|
||||||
for (timer = first_timer; timer != NULL; timer = timer->tr_next)
|
|
||||||
{
|
{
|
||||||
if (timer->tr_paused)
|
timer_next = timer->tr_next;
|
||||||
|
|
||||||
|
if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
|
||||||
continue;
|
continue;
|
||||||
# ifdef WIN3264
|
this_due = GET_TIMEDIFF(timer, now);
|
||||||
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)
|
if (this_due <= 1)
|
||||||
{
|
{
|
||||||
current_timer = timer;
|
timer->tr_firing = TRUE;
|
||||||
free_current_timer = FALSE;
|
|
||||||
timer_callback(timer);
|
timer_callback(timer);
|
||||||
current_timer = NULL;
|
timer->tr_firing = FALSE;
|
||||||
|
timer_next = timer->tr_next;
|
||||||
did_one = TRUE;
|
did_one = TRUE;
|
||||||
if (timer->tr_repeat != 0 && !free_current_timer)
|
|
||||||
|
/* 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);
|
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)
|
if (timer->tr_repeat > 0)
|
||||||
--timer->tr_repeat;
|
--timer->tr_repeat;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
this_due = -1;
|
||||||
remove_timer(timer);
|
remove_timer(timer);
|
||||||
free_timer(timer);
|
free_timer(timer);
|
||||||
}
|
}
|
||||||
/* the callback may do anything, start all over */
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (next_due == -1 || next_due > this_due)
|
if (this_due > 0 && (next_due == -1 || next_due > this_due))
|
||||||
next_due = this_due;
|
next_due = this_due;
|
||||||
}
|
}
|
||||||
if (timer == NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (did_one)
|
if (did_one)
|
||||||
redraw_after_callback();
|
redraw_after_callback();
|
||||||
|
|
||||||
return next_due;
|
return current_id != last_timer_id ? 1 : next_due;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find a timer by ID. Returns NULL if not found;
|
* Find a timer by ID. Returns NULL if not found;
|
||||||
*/
|
*/
|
||||||
timer_T *
|
timer_T *
|
||||||
find_timer(int id)
|
find_timer(long id)
|
||||||
{
|
{
|
||||||
timer_T *timer;
|
timer_T *timer;
|
||||||
|
|
||||||
|
if (id >= 0)
|
||||||
|
{
|
||||||
for (timer = first_timer; timer != NULL; timer = timer->tr_next)
|
for (timer = first_timer; timer != NULL; timer = timer->tr_next)
|
||||||
if (timer->tr_id == id)
|
if (timer->tr_id == id)
|
||||||
break;
|
|
||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1261,16 +1265,28 @@ find_timer(int id)
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
stop_timer(timer_T *timer)
|
stop_timer(timer_T *timer)
|
||||||
|
{
|
||||||
|
if (timer->tr_firing)
|
||||||
|
/* Free the timer after the callback returns. */
|
||||||
|
timer->tr_id = -1;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
remove_timer(timer);
|
remove_timer(timer);
|
||||||
free_timer(timer);
|
free_timer(timer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
stop_all_timers(void)
|
stop_all_timers(void)
|
||||||
{
|
{
|
||||||
while (first_timer != NULL)
|
timer_T *timer;
|
||||||
stop_timer(first_timer);
|
timer_T *timer_next;
|
||||||
|
|
||||||
|
for (timer = first_timer; timer != NULL; timer = timer_next)
|
||||||
|
{
|
||||||
|
timer_next = timer->tr_next;
|
||||||
|
stop_timer(timer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1289,18 +1305,14 @@ add_timer_info(typval_T *rettv, timer_T *timer)
|
|||||||
return;
|
return;
|
||||||
list_append_dict(list, dict);
|
list_append_dict(list, dict);
|
||||||
|
|
||||||
dict_add_nr_str(dict, "id", (long)timer->tr_id, NULL);
|
dict_add_nr_str(dict, "id", timer->tr_id, NULL);
|
||||||
dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL);
|
dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL);
|
||||||
|
|
||||||
profile_start(&now);
|
profile_start(&now);
|
||||||
# ifdef WIN3264
|
# ifdef WIN3264
|
||||||
QueryPerformanceFrequency(&fr);
|
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
|
# endif
|
||||||
|
remaining = GET_TIMEDIFF(timer, now);
|
||||||
dict_add_nr_str(dict, "remaining", (long)remaining, NULL);
|
dict_add_nr_str(dict, "remaining", (long)remaining, NULL);
|
||||||
|
|
||||||
dict_add_nr_str(dict, "repeat",
|
dict_add_nr_str(dict, "repeat",
|
||||||
@@ -1333,6 +1345,7 @@ add_timer_info_all(typval_T *rettv)
|
|||||||
timer_T *timer;
|
timer_T *timer;
|
||||||
|
|
||||||
for (timer = first_timer; timer != NULL; timer = timer->tr_next)
|
for (timer = first_timer; timer != NULL; timer = timer->tr_next)
|
||||||
|
if (timer->tr_id != -1)
|
||||||
add_timer_info(rettv, timer);
|
add_timer_info(rettv, timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@ int profile_passed_limit(proftime_T *tm);
|
|||||||
void profile_zero(proftime_T *tm);
|
void profile_zero(proftime_T *tm);
|
||||||
timer_T *create_timer(long msec, int repeat);
|
timer_T *create_timer(long msec, int repeat);
|
||||||
long check_due_timer(void);
|
long check_due_timer(void);
|
||||||
timer_T *find_timer(int id);
|
timer_T *find_timer(long id);
|
||||||
void stop_timer(timer_T *timer);
|
void stop_timer(timer_T *timer);
|
||||||
void stop_all_timers(void);
|
void stop_all_timers(void);
|
||||||
void add_timer_info(typval_T *rettv, timer_T *timer);
|
void add_timer_info(typval_T *rettv, timer_T *timer);
|
||||||
|
@@ -3166,12 +3166,13 @@ typedef struct js_reader js_read_T;
|
|||||||
typedef struct timer_S timer_T;
|
typedef struct timer_S timer_T;
|
||||||
struct timer_S
|
struct timer_S
|
||||||
{
|
{
|
||||||
int tr_id;
|
long tr_id;
|
||||||
#ifdef FEAT_TIMERS
|
#ifdef FEAT_TIMERS
|
||||||
timer_T *tr_next;
|
timer_T *tr_next;
|
||||||
timer_T *tr_prev;
|
timer_T *tr_prev;
|
||||||
proftime_T tr_due; /* when the callback is to be invoked */
|
proftime_T tr_due; /* when the callback is to be invoked */
|
||||||
int tr_paused; /* when TRUE callback is not 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 */
|
int tr_repeat; /* number of times to repeat, -1 forever */
|
||||||
long tr_interval; /* msec */
|
long tr_interval; /* msec */
|
||||||
char_u *tr_callback; /* allocated */
|
char_u *tr_callback; /* allocated */
|
||||||
|
@@ -143,4 +143,34 @@ func Test_delete_myself()
|
|||||||
call assert_equal([], timer_info(t))
|
call assert_equal([], timer_info(t))
|
||||||
endfunc
|
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: shiftwidth=2 sts=2 expandtab
|
||||||
|
@@ -763,6 +763,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2332,
|
||||||
/**/
|
/**/
|
||||||
2331,
|
2331,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user