changeset 10309:88331ee68367 v8.0.0050

commit https://github.com/vim/vim/commit/01688ad545ff0809ddad5c8fa6b149dc5d67312b Author: Bram Moolenaar <Bram@vim.org> Date: Thu Oct 27 20:00:07 2016 +0200 patch 8.0.0050 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.
author Christian Brabandt <cb@256bit.org>
date Thu, 27 Oct 2016 20:15:04 +0200
parents c6e8a776a1ed
children 4819ab41aec5
files src/channel.c src/os_unix.c src/testdir/shared.vim src/testdir/test_channel.vim src/version.c
diffstat 5 files changed, 156 insertions(+), 119 deletions(-) [+]
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -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;
 }
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -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;
-
-	    /* 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;
-	    }
-#endif
-#if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
-	}
-#endif
-	if (wtime >= 0)
-	    /* no character available within "wtime" */
-	    return 0;
-
-	/* wtime == -1: no character available within 'updatetime' */
-#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;
-	}
-#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 */
+	/* Check if window changed size while we were busy, perhaps the ":set
+	 * columns=99" command was used. */
+	while (do_resize)
 	    handle_resize();
 
 #ifdef MESSAGE_QUEUE
 	parse_queued_messages();
-
-# ifdef FEAT_JOB_CHANNEL
-	if (has_pending_job())
+#endif
+	if (wtime < 0 && did_start_blocking)
+	    /* blocking and already waited for p_ut */
+	    wait_time = -1;
+	else
 	{
-	    /* Don't wait longer than a few seconds, checking for a finished
-	     * job requires polling. */
-	    if (p_ut > 9000L)
-		wtime_now = 1000L;
+	    if (wtime >= 0)
+		wait_time = wtime;
 	    else
-		wtime_now = 10000L - p_ut;
+		/* 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
+	    wait_time -= elapsed_time;
+	    if (wait_time < 0)
+	    {
+		if (wtime >= 0)
+		    /* no character available within "wtime" */
+		    return 0;
+
+		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;
+		    }
+#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();
+		    continue;
+		}
+	    }
 	}
-# endif
-#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;
-
-	/*
-	 * 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;
-    }
+	/* no character available */
+#if !(defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H))
+	/* estimate the elapsed time */
+	elapsed += wait_time;
+#endif
+
+	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
--- a/src/testdir/shared.vim
+++ b/src/testdir/shared.vim
@@ -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.
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -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
 
--- 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 */
 /**/
+    50,
+/**/
     49,
 /**/
     48,