To: vim_dev@googlegroups.com Subject: Patch 8.0.0050 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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. Files: src/channel.c, src/os_unix.c, src/testdir/shared.vim, src/testdir/test_channel.vim *** ../vim-8.0.0049/src/channel.c 2016-10-15 18:36:45.341910365 +0200 --- src/channel.c 2016-10-27 19:49:15.597305647 +0200 *************** *** 4643,4650 **** } /* ! * Return TRUE when there is any job that might exit, which means ! * job_check_ended() should be called once in a while. */ int has_pending_job(void) --- 4643,4650 ---- } /* ! * 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,4658 **** job_T *job; for (job = first_job; job != NULL; job = job->jv_next) ! if (job_still_alive(job)) return TRUE; return FALSE; } --- 4652,4662 ---- job_T *job; for (job = first_job; job != NULL; job = job->jv_next) ! /* 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; } *** ../vim-8.0.0049/src/os_unix.c 2016-10-18 16:27:20.544756017 +0200 --- src/os_unix.c 2016-10-27 19:43:31.019404929 +0200 *************** *** 404,542 **** { int len; int interrupted = FALSE; long wait_time; #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(); ! 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 */ - 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 /* * We want to be interrupted by the winch signal * or by an event on the monitored file descriptors. */ ! if (!WaitForChar(wtime_now, &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; } } static void --- 404,524 ---- { 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 ! /* repeat until we got a character or waited long enough */ for (;;) { ! /* 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(); #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(wait_time, &interrupted)) { ! /* 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; } ! /* 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 *** ../vim-8.0.0049/src/testdir/shared.vim 2016-08-22 21:30:05.000000000 +0200 --- src/testdir/shared.vim 2016-10-27 19:51:53.040346452 +0200 *************** *** 136,141 **** --- 136,169 ---- 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. *** ../vim-8.0.0049/src/testdir/test_channel.vim 2016-10-18 16:27:20.544756017 +0200 --- src/testdir/test_channel.vim 2016-10-27 18:09:46.713670034 +0200 *************** *** 1362,1370 **** 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) endfunction func Test_exit_callback_interval() --- 1362,1372 ---- endif endfunc function MyExitTimeCb(job, status) ! 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,1382 **** return endif ! let g:exit_cb_time.start = reltime() 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) call assert_true(elapsed < 1.0) endfunc --- 1374,1403 ---- return endif ! 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'}) ! 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 *** ../vim-8.0.0049/src/version.c 2016-10-27 17:27:38.269074145 +0200 --- src/version.c 2016-10-27 19:54:19.827452177 +0200 *************** *** 766,767 **** --- 766,769 ---- { /* Add new patch number below this line */ + /**/ + 50, /**/ -- CART DRIVER: Bring out your dead! We follow the cart through a wretched, impoverished plague-ridden village. A few starved mongrels run about in the mud scavenging. In the open doorway of one house perhaps we jug glimpse a pair of legs dangling from the ceiling. In another doorway an OLD WOMAN is beating a cat against a wall rather like one does with a mat. The cart passes round a dead donkey or cow in the mud. And a MAN tied to a cart is being hammered to death by four NUNS with huge mallets. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///