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.
diff --git a/src/channel.c b/src/channel.c
index 0bd2378..16156ce 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -4643,8 +4643,8 @@
}
/*
- * 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 @@
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;
}
diff --git a/src/os_unix.c b/src/os_unix.c
index 0569184..6f36553 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -404,139 +404,121 @@
{
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())
- {
- /* 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;
- }
-# endif
#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
+ 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;
+ }
+ }
+ }
+
+#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
diff --git a/src/testdir/shared.vim b/src/testdir/shared.vim
index 24b05be..45a2ea4 100644
--- a/src/testdir/shared.vim
+++ b/src/testdir/shared.vim
@@ -136,6 +136,34 @@
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.
diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim
index 0a50ed4..c21e617 100644
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -1362,9 +1362,11 @@
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 @@
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
diff --git a/src/version.c b/src/version.c
index 6fdf9f4..8386381 100644
--- a/src/version.c
+++ b/src/version.c
@@ -765,6 +765,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 50,
+/**/
49,
/**/
48,