]> granicus.if.org Git - vim/commitdiff
patch 8.0.1761: job in terminal window with no output channel is killed v8.0.1761
authorBram Moolenaar <Bram@vim.org>
Tue, 24 Apr 2018 18:54:07 +0000 (20:54 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 24 Apr 2018 18:54:07 +0000 (20:54 +0200)
Problem:    Job in terminal window with no output channel is killed.
Solution:   Keep the job running when the input is a tty. (Ozaki Kiichi,
            closes #2734)

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

index 359052208ffb1b07ef19f49f3dc183e683090f1d..2c82efa723937431346b707de4cdd21ee9eec6f7 100644 (file)
@@ -344,6 +344,15 @@ channel_still_useful(channel_T *channel)
                    && has_err_msg);
 }
 
+/*
+ * Return TRUE if "channel" is closeable (i.e. all readable fds are closed).
+ */
+    static int
+channel_can_close(channel_T *channel)
+{
+    return channel->ch_to_be_closed == 0;
+}
+
 /*
  * Close a channel and free all its resources.
  */
@@ -892,7 +901,7 @@ channel_open(
     channel->ch_nb_close_cb = nb_close_cb;
     channel->ch_hostname = (char *)vim_strsave((char_u *)hostname);
     channel->ch_port = port_in;
-    channel->ch_to_be_closed |= (1 << PART_SOCK);
+    channel->ch_to_be_closed |= (1U << PART_SOCK);
 
 #ifdef FEAT_GUI
     channel_gui_register_one(channel, PART_SOCK);
@@ -988,7 +997,8 @@ ch_close_part(channel_T *channel, ch_part_T part)
        }
        *fd = INVALID_FD;
 
-       channel->ch_to_be_closed &= ~(1 << part);
+       /* channel is closed, may want to end the job if it was the last */
+       channel->ch_to_be_closed &= ~(1U << part);
     }
 }
 
@@ -999,6 +1009,12 @@ channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err)
     {
        ch_close_part(channel, PART_IN);
        channel->CH_IN_FD = in;
+# if defined(UNIX)
+       /* Do not end the job when all output channels are closed, wait until
+        * the job ended. */
+       if (isatty(in))
+           channel->ch_to_be_closed |= (1U << PART_IN);
+# endif
     }
     if (out != INVALID_FD)
     {
@@ -1007,7 +1023,7 @@ channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err)
 # endif
        ch_close_part(channel, PART_OUT);
        channel->CH_OUT_FD = out;
-       channel->ch_to_be_closed |= (1 << PART_OUT);
+       channel->ch_to_be_closed |= (1U << PART_OUT);
 # if defined(FEAT_GUI)
        channel_gui_register_one(channel, PART_OUT);
 # endif
@@ -1019,7 +1035,7 @@ channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err)
 # endif
        ch_close_part(channel, PART_ERR);
        channel->CH_ERR_FD = err;
-       channel->ch_to_be_closed |= (1 << PART_ERR);
+       channel->ch_to_be_closed |= (1U << PART_ERR);
 # if defined(FEAT_GUI)
        channel_gui_register_one(channel, PART_ERR);
 # endif
@@ -4200,9 +4216,9 @@ channel_parse_messages(void)
     }
     while (channel != NULL)
     {
-       if (channel->ch_to_be_closed == 0)
+       if (channel_can_close(channel))
        {
-           channel->ch_to_be_closed = (1 << PART_COUNT);
+           channel->ch_to_be_closed = (1U << PART_COUNT);
            channel_close_now(channel);
            /* channel may have been freed, start over */
            channel = first_channel;
@@ -5079,6 +5095,15 @@ job_channel_still_useful(job_T *job)
     return job->jv_channel != NULL && channel_still_useful(job->jv_channel);
 }
 
+/*
+ * Return TRUE if the channel of "job" is closeable.
+ */
+    static int
+job_channel_can_close(job_T *job)
+{
+    return job->jv_channel != NULL && channel_can_close(job->jv_channel);
+}
+
 /*
  * Return TRUE if the job should not be freed yet.  Do not free the job when
  * it has not ended yet and there is a "stoponexit" flag, an exit callback
@@ -5209,6 +5234,10 @@ job_cleanup(job_T *job)
     /* Ready to cleanup the job. */
     job->jv_status = JOB_FINISHED;
 
+    /* When only channel-in is kept open, close explicitly. */
+    if (job->jv_channel != NULL)
+       ch_close_part(job->jv_channel, PART_IN);
+
     if (job->jv_exit_cb != NULL)
     {
        typval_T        argv[3];
@@ -5413,8 +5442,9 @@ has_pending_job(void)
     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_channel_still_useful(job))
+       if ((job->jv_status == JOB_STARTED && !job_channel_still_useful(job))
+                   || (job->jv_status == JOB_FINISHED
+                                             && job_channel_can_close(job)))
            return TRUE;
     return FALSE;
 }
index c89131fa3ea87a7dc38fee7cb98c8d37e933c7b8..d20c94b9aca5aabd6a294fe264ac38fc36b046b9 100644 (file)
@@ -5638,13 +5638,14 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options)
        close(fd_err[1]);
     if (channel != NULL)
     {
-       channel_set_pipes(channel,
-               use_file_for_in || use_null_for_in
-                       ? INVALID_FD : fd_in[1] < 0 ? pty_master_fd : fd_in[1],
-               use_file_for_out || use_null_for_out
-                     ? INVALID_FD : fd_out[0] < 0 ? pty_master_fd : fd_out[0],
-               use_out_for_err || use_file_for_err || use_null_for_err
-                    ? INVALID_FD : fd_err[0] < 0 ? pty_master_fd : fd_err[0]);
+       int in_fd = use_file_for_in || use_null_for_in
+                       ? INVALID_FD : fd_in[1] < 0 ? pty_master_fd : fd_in[1];
+       int out_fd = use_file_for_out || use_null_for_out
+                     ? INVALID_FD : fd_out[0] < 0 ? pty_master_fd : fd_out[0];
+       int err_fd = use_out_for_err || use_file_for_err || use_null_for_err
+                     ? INVALID_FD : fd_err[0] < 0 ? pty_master_fd : fd_err[0];
+
+       channel_set_pipes(channel, in_fd, out_fd, err_fd);
        channel_set_job(channel, job, options);
     }
     else
index 805fa8dcaceee94264cb6895078780f5157a6b46..ec8111e583fa4acf04dfe1c1abe53ae93232df0e 100644 (file)
@@ -1848,3 +1848,14 @@ func Test_zz_ch_log()
   call assert_match("%s%s", text[2])
   call delete('Xlog')
 endfunc
+
+func Test_keep_pty_open()
+  if !has('unix')
+    return
+  endif
+
+  let job = job_start(s:python . ' -c "import time;time.sleep(0.2)"', {'out_io': 'null', 'err_io': 'null', 'pty': 1})
+  let elapsed = WaitFor({-> job_status(job) ==# 'dead'})
+  call assert_inrange(200, 1000, elapsed)
+  call job_stop(job)
+endfunc
index f16a4a7b204f23b8922f99817dca9dee824afc72..5d63a3b5fa23757afb4e897b79c2422587e53f00 100644 (file)
@@ -761,6 +761,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1761,
 /**/
     1760,
 /**/