]> granicus.if.org Git - vim/commitdiff
patch 8.0.0702: an error in a timer can make Vim unusable v8.0.0702
authorBram Moolenaar <Bram@vim.org>
Sat, 8 Jul 2017 20:37:34 +0000 (22:37 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 8 Jul 2017 20:37:34 +0000 (22:37 +0200)
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.

runtime/doc/eval.txt
src/ex_cmds2.c
src/structs.h
src/testdir/test_timers.vim
src/version.c

index 03f59a9595db7a13eef4a836c6f30c03e714b5d4..781c79af01c427373977d427258243df9073239d 100644 (file)
@@ -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)
index 3efa8ca2e5be1afa3f6509f5bfd79dd27b64ffce..76e62f7a0be5ea6734b46bd8c74a0116b8054913 100644 (file)
@@ -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);
index 0fbc5a5bdab2fa766f865d771c3a0a9bfa8e6de8..c3f120008c131fc03f2eace090c8fb6f07b7dc5d 100644 (file)
@@ -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
 };
 
index fdb74e79581a04be53fbb0bc068a11eaef12d4c0..d30325b7bbcdf3b5d410e02f428736dcb7b9def8 100644 (file)
@@ -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
index 42fb71da7b5d0c82bfb465eac1d7795d07e9eadd..70de1d4b7a6a95f64566a49d32b3810d926afa53 100644 (file)
@@ -764,6 +764,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    702,
 /**/
     701,
 /**/