# HG changeset patch # User Christian Brabandt # Date 1499546703 -7200 # Node ID 71d7b5ed08a0d863e6b1368d142d4bb6e983bd42 # Parent 8bac0b53882b3c895cefb3a887e14e5d91c54062 patch 8.0.0702: an error in a timer can make Vim unusable commit https://github.com/vim/vim/commit/c577d813b7978345dec4310b2d8f5d5624a681f6 Author: Bram Moolenaar Date: Sat Jul 8 22:37:34 2017 +0200 patch 8.0.0702: an error in a timer can make Vim unusable 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. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.0. Last change: 2017 Jun 25 +*eval.txt* For Vim version 8.0. Last change: 2017 Jul 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -4189,14 +4189,14 @@ getchar([expr]) *getchar()* not consumed. Return zero if no character available. 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. Otherwise a String is returned with the encoded character. - For a special key it's a sequence of bytes starting with 0x80 - (decimal: 128). This is the same value as the string - "\", e.g., "\". The returned value is also a - String when a modifier (shift, control, alt) was used that is - not included in the character. + For a special key it's a String with a sequence of bytes + starting with 0x80 (decimal: 128). This is the same value as + the String "\", e.g., "\". The returned value is + also a String when a modifier (shift, control, alt) was used + that is not included in the character. 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 @@ -8017,6 +8017,10 @@ timer_start({time}, {callback} [, {optio "repeat" Number of times to repeat calling the callback. -1 means forever. When not present 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: > func MyHandler(timer) diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -1197,11 +1197,13 @@ check_due_timer(void) long current_id = last_timer_id; # ifdef WIN3264 LARGE_INTEGER fr; - - /* Don't run any timers while exiting. */ - if (exiting) +# endif + + /* Don't run any timers while exiting or dealing with an error. */ + if (exiting || aborting()) return next_due; +# ifdef WIN3264 QueryPerformanceFrequency(&fr); # endif profile_start(&now); @@ -1216,9 +1218,13 @@ check_due_timer(void) { int save_timer_busy = timer_busy; int save_vgetc_busy = vgetc_busy; + int did_emsg_save = did_emsg; + int called_emsg_save = called_emsg; + int did_throw_save = did_throw; timer_busy = timer_busy > 0 || vgetc_busy > 0; vgetc_busy = 0; + called_emsg = FALSE; timer->tr_firing = TRUE; timer_callback(timer); timer->tr_firing = FALSE; @@ -1226,10 +1232,19 @@ check_due_timer(void) did_one = TRUE; timer_busy = save_timer_busy; vgetc_busy = save_vgetc_busy; + if (called_emsg) + { + ++timer->tr_emsg_count; + if (!did_throw_save && current_exception != NULL) + discard_current_exception(); + } + did_emsg = did_emsg_save; + called_emsg = called_emsg_save; /* 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) + if (timer->tr_repeat != 0 && timer->tr_id != -1 + && timer->tr_emsg_count < 3) { profile_setlimit(timer->tr_interval, &timer->tr_due); this_due = GET_TIMEDIFF(timer, now); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -3243,6 +3243,7 @@ struct timer_S long tr_interval; /* msec */ char_u *tr_callback; /* allocated */ partial_T *tr_partial; + int tr_emsg_count; #endif }; diff --git a/src/testdir/test_timers.vim b/src/testdir/test_timers.vim --- a/src/testdir/test_timers.vim +++ b/src/testdir/test_timers.vim @@ -189,4 +189,22 @@ func Test_input_in_timer() call assert_equal('hello', g:val) 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 + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -765,6 +765,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 702, +/**/ 701, /**/ 700,