]> granicus.if.org Git - vim/commitdiff
patch 8.0.0050 v8.0.0050
authorBram Moolenaar <Bram@vim.org>
Thu, 27 Oct 2016 18:00:07 +0000 (20:00 +0200)
committerBram Moolenaar <Bram@vim.org>
Thu, 27 Oct 2016 18:00:07 +0000 (20:00 +0200)
Problem:    An exiting job is detected with a large latency.
Solution:   Check for pending job more often. (Ozaki Kiichi)  Change the
            double loop in mch_inchar() into one.

src/channel.c
src/os_unix.c
src/testdir/shared.vim
src/testdir/test_channel.vim
src/version.c

index 0bd23785e73ffc29e0c9bf1105b712f7dd56ddba..16156ce4191baddcd27a0e29b3debe955145aca7 100644 (file)
@@ -4643,8 +4643,8 @@ job_stop_on_exit(void)
 }
 
 /*
- * Return TRUE when there is any job that might exit, which means
- * job_check_ended() should be called once in a while.
+ * Return TRUE when there is any job that has an exit callback and might exit,
+ * which means job_check_ended() should be called more often.
  */
     int
 has_pending_job(void)
@@ -4652,7 +4652,11 @@ has_pending_job(void)
     job_T          *job;
 
     for (job = first_job; job != NULL; job = job->jv_next)
-       if (job_still_alive(job))
+       /* Only should check if the channel has been closed, if the channel is
+        * open the job won't exit. */
+       if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL
+               && (job->jv_channel == NULL
+                   || !channel_still_useful(job->jv_channel)))
            return TRUE;
     return FALSE;
 }
index 05691841392794e1b139d01df09987fc29986fdd..6f365537c226e22b4b859644b265ccd4342a6d70 100644 (file)
@@ -404,139 +404,121 @@ mch_inchar(
 {
     int                len;
     int                interrupted = FALSE;
+    int                did_start_blocking = FALSE;
     long       wait_time;
+    long       elapsed_time = 0;
 #if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
     struct timeval  start_tv;
 
     gettimeofday(&start_tv, NULL);
 #endif
 
-#ifdef MESSAGE_QUEUE
-    parse_queued_messages();
-#endif
-
-    /* Check if window changed size while we were busy, perhaps the ":set
-     * columns=99" command was used. */
-    while (do_resize)
-       handle_resize();
-
+    /* repeat until we got a character or waited long enough */
     for (;;)
     {
-       if (wtime >= 0)
-           wait_time = wtime;
-       else
-           wait_time = p_ut;
-#if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
-       wait_time -= elapsed(&start_tv);
-       if (wait_time >= 0)
-       {
-#endif
-           if (WaitForChar(wait_time, &interrupted))
-               break;
+       /* Check if window changed size while we were busy, perhaps the ":set
+        * columns=99" command was used. */
+       while (do_resize)
+           handle_resize();
 
-           /* no character available */
-           if (do_resize)
-           {
-               handle_resize();
-               continue;
-           }
-#ifdef FEAT_CLIENTSERVER
-           if (server_waiting())
-           {
-               parse_queued_messages();
-               continue;
-           }
-#endif
 #ifdef MESSAGE_QUEUE
-           if (interrupted)
-           {
-               parse_queued_messages();
-               continue;
-           }
+       parse_queued_messages();
 #endif
+       if (wtime < 0 && did_start_blocking)
+           /* blocking and already waited for p_ut */
+           wait_time = -1;
+       else
+       {
+           if (wtime >= 0)
+               wait_time = wtime;
+           else
+               /* going to block after p_ut */
+               wait_time = p_ut;
 #if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
-       }
+           elapsed_time = elapsed(&start_tv);
 #endif
-       if (wtime >= 0)
-           /* no character available within "wtime" */
-           return 0;
+           wait_time -= elapsed_time;
+           if (wait_time < 0)
+           {
+               if (wtime >= 0)
+                   /* no character available within "wtime" */
+                   return 0;
 
-       /* wtime == -1: no character available within 'updatetime' */
+               if (wtime < 0)
+               {
+                   /* no character available within 'updatetime' */
+                   did_start_blocking = TRUE;
 #ifdef FEAT_AUTOCMD
-       if (trigger_cursorhold() && maxlen >= 3
-                                          && !typebuf_changed(tb_change_cnt))
-       {
-           buf[0] = K_SPECIAL;
-           buf[1] = KS_EXTRA;
-           buf[2] = (int)KE_CURSORHOLD;
-           return 3;
-       }
+                   if (trigger_cursorhold() && maxlen >= 3
+                                           && !typebuf_changed(tb_change_cnt))
+                   {
+                       buf[0] = K_SPECIAL;
+                       buf[1] = KS_EXTRA;
+                       buf[2] = (int)KE_CURSORHOLD;
+                       return 3;
+                   }
 #endif
-       /*
-        * If there is no character available within 'updatetime' seconds
-        * flush all the swap files to disk.
-        * Also done when interrupted by SIGWINCH.
-        */
-       before_blocking();
-       break;
-    }
-
-    /* repeat until we got a character */
-    for (;;)
-    {
-       long    wtime_now = -1L;
-
-       while (do_resize)    /* window changed size */
-           handle_resize();
-
-#ifdef MESSAGE_QUEUE
-       parse_queued_messages();
-
-# ifdef FEAT_JOB_CHANNEL
-       if (has_pending_job())
-       {
-           /* Don't wait longer than a few seconds, checking for a finished
-            * job requires polling. */
-           if (p_ut > 9000L)
-               wtime_now = 1000L;
-           else
-               wtime_now = 10000L - p_ut;
+                   /*
+                    * If there is no character available within 'updatetime'
+                    * seconds flush all the swap files to disk.
+                    * Also done when interrupted by SIGWINCH.
+                    */
+                   before_blocking();
+                   continue;
+               }
+           }
        }
-# endif
+
+#ifdef FEAT_JOB_CHANNEL
+       /* Checking if a job ended requires polling.  Do this every 100 msec. */
+       if (has_pending_job() && (wait_time < 0 || wait_time > 100L))
+           wait_time = 100L;
 #endif
+
        /*
         * We want to be interrupted by the winch signal
         * or by an event on the monitored file descriptors.
         */
-       if (!WaitForChar(wtime_now, &interrupted))
+       if (WaitForChar(wait_time, &interrupted))
        {
-           if (do_resize)          /* interrupted by SIGWINCH signal */
-               continue;
-#ifdef MESSAGE_QUEUE
-           if (interrupted || wtime_now > 0)
-           {
-               parse_queued_messages();
-               continue;
-           }
-#endif
-           return 0;
+           /* If input was put directly in typeahead buffer bail out here. */
+           if (typebuf_changed(tb_change_cnt))
+               return 0;
+
+           /*
+            * For some terminals we only get one character at a time.
+            * We want the get all available characters, so we could keep on
+            * trying until none is available
+            * For some other terminals this is quite slow, that's why we don't
+            * do it.
+            */
+           len = read_from_input_buf(buf, (long)maxlen);
+           if (len > 0)
+               return len;
+           continue;
        }
 
-       /* If input was put directly in typeahead buffer bail out here. */
-       if (typebuf_changed(tb_change_cnt))
-           return 0;
+       /* no character available */
+#if !(defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H))
+       /* estimate the elapsed time */
+       elapsed += wait_time;
+#endif
 
-       /*
-        * For some terminals we only get one character at a time.
-        * We want the get all available characters, so we could keep on
-        * trying until none is available
-        * For some other terminals this is quite slow, that's why we don't do
-        * it.
-        */
-       len = read_from_input_buf(buf, (long)maxlen);
-       if (len > 0)
-           return len;
+       if (do_resize       /* interrupted by SIGWINCH signal */
+#ifdef FEAT_CLIENTSERVER
+               || server_waiting()
+#endif
+#ifdef MESSAGE_QUEUE
+               || interrupted
+#endif
+               || wait_time > 0
+               || !did_start_blocking)
+           continue;
+
+       /* no character available or interrupted */
+       break;
     }
+    return 0;
 }
 
     static void
index 24b05bec6a1a16f69f740b227f283c2171087d88..45a2ea496dadcabfcc4d44ab6e4cdfaacbba0789 100644 (file)
@@ -136,6 +136,34 @@ func WaitFor(expr)
   return 1000
 endfunc
 
+" Wait for up to a given milliseconds.
+" With the +timers feature this waits for key-input by getchar(), Resume()
+" feeds key-input and resumes process. Return time waited in milliseconds.
+" Without +timers it uses simply :sleep.
+func Standby(msec)
+  if has('timers')
+    let start = reltime()
+    let g:_standby_timer = timer_start(a:msec, function('s:feedkeys'))
+    call getchar()
+    return float2nr(reltimefloat(reltime(start)) * 1000)
+  else
+    execute 'sleep ' a:msec . 'm'
+    return a:msec
+  endif
+endfunc
+
+func Resume()
+  if exists('g:_standby_timer')
+    call timer_stop(g:_standby_timer)
+    call s:feedkeys(0)
+    unlet g:_standby_timer
+  endif
+endfunc
+
+func s:feedkeys(timer)
+  call feedkeys('x', 'nt')
+endfunc
+
 " Run Vim, using the "vimcmd" file and "-u NORC".
 " "before" is a list of Vim commands to be executed before loading plugins.
 " "after" is a list of Vim commands to be executed after loading plugins.
index 0a50ed47e458b83459ea11b2c9225b9b761bce33..c21e617b1e362bd43d79fd6845e1a9faacaec200 100644 (file)
@@ -1362,9 +1362,11 @@ func Test_exit_callback()
   endif
 endfunc
 
-let g:exit_cb_time = {'start': 0, 'end': 0}
 function MyExitTimeCb(job, status)
-  let g:exit_cb_time.end = reltime(g:exit_cb_time.start)
+  if job_info(a:job).process == g:exit_cb_val.process
+    let g:exit_cb_val.end = reltime(g:exit_cb_val.start)
+  endif
+  call Resume()
 endfunction
 
 func Test_exit_callback_interval()
@@ -1372,11 +1374,30 @@ func Test_exit_callback_interval()
     return
   endif
 
-  let g:exit_cb_time.start = reltime()
+  let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0}
   let job = job_start([s:python, '-c', 'import time;time.sleep(0.5)'], {'exit_cb': 'MyExitTimeCb'})
-  call WaitFor('g:exit_cb_time.end != 0')
-  let elapsed = reltimefloat(g:exit_cb_time.end)
-  call assert_true(elapsed > 0.3)
+  let g:exit_cb_val.process = job_info(job).process
+  call WaitFor('type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0')
+  let elapsed = reltimefloat(g:exit_cb_val.end)
+  call assert_true(elapsed > 0.5)
+  call assert_true(elapsed < 1.0)
+
+  " case: unreferenced job, using timer
+  if !has('timers')
+    return
+  endif
+
+  let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0}
+  let g:job = job_start([s:python, '-c', 'import time;time.sleep(0.5)'], {'exit_cb': 'MyExitTimeCb'})
+  let g:exit_cb_val.process = job_info(g:job).process
+  unlet g:job
+  call Standby(1000)
+  if type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0
+    let elapsed = reltimefloat(g:exit_cb_val.end)
+  else
+    let elapsed = 1.0
+  endif
+  call assert_true(elapsed > 0.5)
   call assert_true(elapsed < 1.0)
 endfunc
 
index 6fdf9f42ecc1a5faf0a4bb034d1e686587326526..8386381b7c020de92ee59400378467c84684c5ba 100644 (file)
@@ -764,6 +764,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    50,
 /**/
     49,
 /**/