Skip to content

Commit c577d81

Browse files
committed
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.
1 parent 11e79bb commit c577d81

File tree

5 files changed

+50
-10
lines changed

5 files changed

+50
-10
lines changed

runtime/doc/eval.txt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*eval.txt* For Vim version 8.0. Last change: 2017 Jun 25
1+
*eval.txt* For Vim version 8.0. Last change: 2017 Jul 08
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -4189,14 +4189,14 @@ getchar([expr]) *getchar()*
41894189
not consumed. Return zero if no character available.
41904190

41914191
Without [expr] and when [expr] is 0 a whole character or
4192-
special key is returned. If it is an 8-bit character, the
4192+
special key is returned. If it is a single character, the
41934193
result is a number. Use nr2char() to convert it to a String.
41944194
Otherwise a String is returned with the encoded character.
4195-
For a special key it's a sequence of bytes starting with 0x80
4196-
(decimal: 128). This is the same value as the string
4197-
"\<Key>", e.g., "\<Left>". The returned value is also a
4198-
String when a modifier (shift, control, alt) was used that is
4199-
not included in the character.
4195+
For a special key it's a String with a sequence of bytes
4196+
starting with 0x80 (decimal: 128). This is the same value as
4197+
the String "\<Key>", e.g., "\<Left>". The returned value is
4198+
also a String when a modifier (shift, control, alt) was used
4199+
that is not included in the character.
42004200

42014201
When [expr] is 0 and Esc is typed, there will be a short delay
42024202
while Vim waits to see if this is the start of an escape
@@ -8017,6 +8017,10 @@ timer_start({time}, {callback} [, {options}])
80178017
"repeat" Number of times to repeat calling the
80188018
callback. -1 means forever. When not present
80198019
the callback will be called once.
8020+
If the timer causes an error three times in a
8021+
row the repeat is cancelled. This avoids that
8022+
Vim becomes unusable because of all the error
8023+
messages.
80208024

80218025
Example: >
80228026
func MyHandler(timer)

src/ex_cmds2.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,11 +1197,13 @@ check_due_timer(void)
11971197
long current_id = last_timer_id;
11981198
# ifdef WIN3264
11991199
LARGE_INTEGER fr;
1200+
# endif
12001201

1201-
/* Don't run any timers while exiting. */
1202-
if (exiting)
1202+
/* Don't run any timers while exiting or dealing with an error. */
1203+
if (exiting || aborting())
12031204
return next_due;
12041205

1206+
# ifdef WIN3264
12051207
QueryPerformanceFrequency(&fr);
12061208
# endif
12071209
profile_start(&now);
@@ -1216,20 +1218,33 @@ check_due_timer(void)
12161218
{
12171219
int save_timer_busy = timer_busy;
12181220
int save_vgetc_busy = vgetc_busy;
1221+
int did_emsg_save = did_emsg;
1222+
int called_emsg_save = called_emsg;
1223+
int did_throw_save = did_throw;
12191224

12201225
timer_busy = timer_busy > 0 || vgetc_busy > 0;
12211226
vgetc_busy = 0;
1227+
called_emsg = FALSE;
12221228
timer->tr_firing = TRUE;
12231229
timer_callback(timer);
12241230
timer->tr_firing = FALSE;
12251231
timer_next = timer->tr_next;
12261232
did_one = TRUE;
12271233
timer_busy = save_timer_busy;
12281234
vgetc_busy = save_vgetc_busy;
1235+
if (called_emsg)
1236+
{
1237+
++timer->tr_emsg_count;
1238+
if (!did_throw_save && current_exception != NULL)
1239+
discard_current_exception();
1240+
}
1241+
did_emsg = did_emsg_save;
1242+
called_emsg = called_emsg_save;
12291243

12301244
/* Only fire the timer again if it repeats and stop_timer() wasn't
12311245
* called while inside the callback (tr_id == -1). */
1232-
if (timer->tr_repeat != 0 && timer->tr_id != -1)
1246+
if (timer->tr_repeat != 0 && timer->tr_id != -1
1247+
&& timer->tr_emsg_count < 3)
12331248
{
12341249
profile_setlimit(timer->tr_interval, &timer->tr_due);
12351250
this_due = GET_TIMEDIFF(timer, now);

src/structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3243,6 +3243,7 @@ struct timer_S
32433243
long tr_interval; /* msec */
32443244
char_u *tr_callback; /* allocated */
32453245
partial_T *tr_partial;
3246+
int tr_emsg_count;
32463247
#endif
32473248
};
32483249

src/testdir/test_timers.vim

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,22 @@ func Test_input_in_timer()
189189
call assert_equal('hello', g:val)
190190
endfunc
191191

192+
func FuncWithError(timer)
193+
let g:call_count += 1
194+
if g:call_count == 4
195+
return
196+
endif
197+
doesnotexist
198+
endfunc
199+
200+
func Test_timer_errors()
201+
let g:call_count = 0
202+
let timer = timer_start(10, 'FuncWithError', {'repeat': -1})
203+
" Timer will be stopped after failing 3 out of 3 times.
204+
call WaitFor('g:call_count == 3')
205+
sleep 50m
206+
call assert_equal(3, g:call_count)
207+
endfunc
208+
209+
192210
" vim: shiftwidth=2 sts=2 expandtab

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,8 @@ static char *(features[]) =
764764

765765
static int included_patches[] =
766766
{ /* Add new patch number below this line */
767+
/**/
768+
702,
767769
/**/
768770
701,
769771
/**/

0 commit comments

Comments
 (0)