summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2017-07-08 22:37:34 +0200
committerBram Moolenaar <Bram@vim.org>2017-07-08 22:37:34 +0200
commitc577d813b7978345dec4310b2d8f5d5624a681f6 (patch)
tree662bfda985692d778505c74d5c512201e1265362
parent11e79bb04ea635d180dd79b1d5cbc755b56e66e1 (diff)
patch 8.0.0702: an error in a timer can make Vim unusablev8.0.0702
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.
-rw-r--r--runtime/doc/eval.txt18
-rw-r--r--src/ex_cmds2.c21
-rw-r--r--src/structs.h1
-rw-r--r--src/testdir/test_timers.vim18
-rw-r--r--src/version.c2
5 files changed, 50 insertions, 10 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 03f59a9595..781c79af01 100644
--- 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
- "\<Key>", e.g., "\<Left>". 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 "\<Key>", e.g., "\<Left>". 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} [, {options}])
"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
index 3efa8ca2e5..76e62f7a0b 100644
--- 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;
+# endif
- /* Don't run any timers while exiting. */
- if (exiting)
+ /* 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
index 0fbc5a5bda..c3f120008c 100644
--- 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
index fdb74e7958..d30325b7bb 100644
--- 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
index 42fb71da7b..70de1d4b7a 100644
--- 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,