mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:8.0.0702: error in a timer can make Vim unusable #9826
Problem: An error in a timer can make Vim unusable.
Solution: Don't set the error flag or exception from a timer. Stop a timer
if it causes an error 3 out of 3 times. Discard an exception
caused inside a timer.
c577d813b7
closes #9826
This commit is contained in:
parent
ae2401621a
commit
9d085c75ff
@ -4007,14 +4007,14 @@ getchar([expr]) *getchar()*
|
|||||||
not consumed. Return zero if no character available.
|
not consumed. Return zero if no character available.
|
||||||
|
|
||||||
Without [expr] and when [expr] is 0 a whole character or
|
Without [expr] and when [expr] is 0 a whole character or
|
||||||
special key is returned. If it is an 8-bit character, the
|
special key is returned. If it is a single character, the
|
||||||
result is a number. Use nr2char() to convert it to a String.
|
result is a number. Use nr2char() to convert it to a String.
|
||||||
Otherwise a String is returned with the encoded character.
|
Otherwise a String is returned with the encoded character.
|
||||||
For a special key it's a sequence of bytes starting with 0x80
|
For a special key it's a String with a sequence of bytes
|
||||||
(decimal: 128). This is the same value as the string
|
starting with 0x80 (decimal: 128). This is the same value as
|
||||||
"\<Key>", e.g., "\<Left>". The returned value is also a
|
the String "\<Key>", e.g., "\<Left>". The returned value is
|
||||||
String when a modifier (shift, control, alt) was used that is
|
also a String when a modifier (shift, control, alt) was used
|
||||||
not included in the character.
|
that is not included in the character.
|
||||||
|
|
||||||
When [expr] is 0 and Esc is typed, there will be a short delay
|
When [expr] is 0 and Esc is typed, there will be a short delay
|
||||||
while Vim waits to see if this is the start of an escape
|
while Vim waits to see if this is the start of an escape
|
||||||
@ -8096,6 +8096,10 @@ timer_start({time}, {callback} [, {options}])
|
|||||||
"repeat" Number of times to repeat calling the
|
"repeat" Number of times to repeat calling the
|
||||||
callback. -1 means forever. When not present
|
callback. -1 means forever. When not present
|
||||||
the callback will be called once.
|
the callback will be called once.
|
||||||
|
If the timer causes an error three times in a
|
||||||
|
row the repeat is cancelled. This avoids that
|
||||||
|
Vim becomes unusable because of all the error
|
||||||
|
messages.
|
||||||
|
|
||||||
Example: >
|
Example: >
|
||||||
func MyHandler(timer)
|
func MyHandler(timer)
|
||||||
|
@ -449,6 +449,7 @@ typedef struct {
|
|||||||
int timer_id;
|
int timer_id;
|
||||||
int repeat_count;
|
int repeat_count;
|
||||||
int refcount;
|
int refcount;
|
||||||
|
int emsg_count; ///< Errors in a repeating timer.
|
||||||
long timeout;
|
long timeout;
|
||||||
bool stopped;
|
bool stopped;
|
||||||
bool paused;
|
bool paused;
|
||||||
@ -17160,6 +17161,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
timer->refcount = 1;
|
timer->refcount = 1;
|
||||||
timer->stopped = false;
|
timer->stopped = false;
|
||||||
timer->paused = false;
|
timer->paused = false;
|
||||||
|
timer->emsg_count = 0;
|
||||||
timer->repeat_count = repeat;
|
timer->repeat_count = repeat;
|
||||||
timer->timeout = timeout;
|
timer->timeout = timeout;
|
||||||
timer->timer_id = last_timer_id++;
|
timer->timer_id = last_timer_id++;
|
||||||
@ -17202,6 +17204,9 @@ static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr)
|
|||||||
static void timer_due_cb(TimeWatcher *tw, void *data)
|
static void timer_due_cb(TimeWatcher *tw, void *data)
|
||||||
{
|
{
|
||||||
timer_T *timer = (timer_T *)data;
|
timer_T *timer = (timer_T *)data;
|
||||||
|
int save_did_emsg = did_emsg;
|
||||||
|
int save_called_emsg = called_emsg;
|
||||||
|
|
||||||
if (timer->stopped || timer->paused) {
|
if (timer->stopped || timer->paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -17216,8 +17221,24 @@ static void timer_due_cb(TimeWatcher *tw, void *data)
|
|||||||
argv[0].v_type = VAR_NUMBER;
|
argv[0].v_type = VAR_NUMBER;
|
||||||
argv[0].vval.v_number = timer->timer_id;
|
argv[0].vval.v_number = timer->timer_id;
|
||||||
typval_T rettv = TV_INITIAL_VALUE;
|
typval_T rettv = TV_INITIAL_VALUE;
|
||||||
|
called_emsg = false;
|
||||||
|
|
||||||
callback_call(&timer->callback, 1, argv, &rettv);
|
callback_call(&timer->callback, 1, argv, &rettv);
|
||||||
|
|
||||||
|
// Handle error message
|
||||||
|
if (called_emsg && did_emsg) {
|
||||||
|
timer->emsg_count++;
|
||||||
|
if (current_exception != NULL) {
|
||||||
|
discard_current_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
did_emsg = save_did_emsg;
|
||||||
|
called_emsg = save_called_emsg;
|
||||||
|
|
||||||
|
if (timer->emsg_count >= 3) {
|
||||||
|
timer_stop(timer);
|
||||||
|
}
|
||||||
|
|
||||||
tv_clear(&rettv);
|
tv_clear(&rettv);
|
||||||
|
|
||||||
if (!timer->stopped && timer->timeout == 0) {
|
if (!timer->stopped && timer->timeout == 0) {
|
||||||
|
@ -3383,7 +3383,7 @@ ambw_end:
|
|||||||
check_redraw(options[opt_idx].flags);
|
check_redraw(options[opt_idx].flags);
|
||||||
|
|
||||||
return errmsg;
|
return errmsg;
|
||||||
}
|
} // NOLINT(readability/fn_size)
|
||||||
|
|
||||||
/// Simple int comparison function for use with qsort()
|
/// Simple int comparison function for use with qsort()
|
||||||
static int int_cmp(const void *a, const void *b)
|
static int int_cmp(const void *a, const void *b)
|
||||||
|
@ -200,6 +200,23 @@ func Test_input_in_timer()
|
|||||||
call assert_equal('hello', g:val)
|
call assert_equal('hello', g:val)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func FuncWithError(timer)
|
||||||
|
let g:call_count += 1
|
||||||
|
if g:call_count == 4
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
doesnotexist
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_timer_errors()
|
||||||
|
let g:call_count = 0
|
||||||
|
let timer = timer_start(10, 'FuncWithError', {'repeat': -1})
|
||||||
|
" Timer will be stopped after failing 3 out of 3 times.
|
||||||
|
call WaitFor('g:call_count == 3')
|
||||||
|
sleep 50m
|
||||||
|
call assert_equal(3, g:call_count)
|
||||||
|
endfunc
|
||||||
|
|
||||||
func FuncWithCaughtError(timer)
|
func FuncWithCaughtError(timer)
|
||||||
let g:call_count += 1
|
let g:call_count += 1
|
||||||
try
|
try
|
||||||
|
Loading…
Reference in New Issue
Block a user