Patch 8.0.0027
Problem:    A channel is closed when reading on stderr or stdout fails, but
            there may still be something to read on another part.
Solution:   Turn ch_to_be_closed into a bitfield. (Ozaki Kiichi)
Files:      src/channel.c, src/eval.c, src/structs.h, src/proto/channel.pro,
            src/testdir/test_channel.vim


*** ../vim-8.0.0026/src/channel.c       2016-10-09 15:43:22.455026647 +0200
--- src/channel.c       2016-10-09 17:08:57.303345580 +0200
***************
*** 54,60 ****
  # define fd_close(sd) close(sd)
  #endif
  
! static void channel_read(channel_T *channel, int part, char *func);
  
  /* Whether a redraw is needed for appending a line to a buffer. */
  static int channel_need_redraw = FALSE;
--- 54,60 ----
  # define fd_close(sd) close(sd)
  #endif
  
! static void channel_read(channel_T *channel, ch_part_T part, char *func);
  
  /* Whether a redraw is needed for appending a line to a buffer. */
  static int channel_need_redraw = FALSE;
***************
*** 309,315 ****
      channel_T *
  add_channel(void)
  {
!     int               part;
      channel_T *channel = (channel_T *)alloc_clear((int)sizeof(channel_T));
  
      if (channel == NULL)
--- 309,315 ----
      channel_T *
  add_channel(void)
  {
!     ch_part_T part;
      channel_T *channel = (channel_T *)alloc_clear((int)sizeof(channel_T));
  
      if (channel == NULL)
***************
*** 318,324 ****
      channel->ch_id = next_ch_id++;
      ch_log(channel, "Created channel");
  
!     for (part = PART_SOCK; part <= PART_IN; ++part)
      {
        channel->ch_part[part].ch_fd = INVALID_FD;
  #ifdef FEAT_GUI_X11
--- 318,324 ----
      channel->ch_id = next_ch_id++;
      ch_log(channel, "Created channel");
  
!     for (part = PART_SOCK; part < PART_COUNT; ++part)
      {
        channel->ch_part[part].ch_fd = INVALID_FD;
  #ifdef FEAT_GUI_X11
***************
*** 421,429 ****
      if (!in_free_unref_items)
      {
        if (safe_to_invoke_callback == 0)
-       {
            channel->ch_to_be_freed = TRUE;
-       }
        else
        {
            channel_free_contents(channel);
--- 421,427 ----
***************
*** 511,517 ****
  channel_read_fd(int fd)
  {
      channel_T *channel;
!     int               part;
  
      channel = channel_fd2channel(fd, &part);
      if (channel == NULL)
--- 509,515 ----
  channel_read_fd(int fd)
  {
      channel_T *channel;
!     ch_part_T part;
  
      channel = channel_fd2channel(fd, &part);
      if (channel == NULL)
***************
*** 557,563 ****
  #endif
  
      static void
! channel_gui_register_one(channel_T *channel, int part)
  {
      if (!CH_HAS_GUI)
        return;
--- 555,561 ----
  #endif
  
      static void
! channel_gui_register_one(channel_T *channel, ch_part_T part)
  {
      if (!CH_HAS_GUI)
        return;
***************
*** 627,633 ****
  }
  
      static void
! channel_gui_unregister_one(channel_T *channel, int part)
  {
  # ifdef FEAT_GUI_X11
      if (channel->ch_part[part].ch_inputHandler != (XtInputId)NULL)
--- 625,631 ----
  }
  
      static void
! channel_gui_unregister_one(channel_T *channel, ch_part_T part)
  {
  # ifdef FEAT_GUI_X11
      if (channel->ch_part[part].ch_inputHandler != (XtInputId)NULL)
***************
*** 653,659 ****
      static void
  channel_gui_unregister(channel_T *channel)
  {
!     int           part;
  
      for (part = PART_SOCK; part < PART_IN; ++part)
        channel_gui_unregister_one(channel, part);
--- 651,657 ----
      static void
  channel_gui_unregister(channel_T *channel)
  {
!     ch_part_T part;
  
      for (part = PART_SOCK; part < PART_IN; ++part)
        channel_gui_unregister_one(channel, part);
***************
*** 928,933 ****
--- 926,932 ----
      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);
  
  #ifdef FEAT_GUI
      channel_gui_register_one(channel, PART_SOCK);
***************
*** 998,1009 ****
  }
  
      static void
! may_close_part(sock_T *fd)
  {
      if (*fd != INVALID_FD)
      {
!       fd_close(*fd);
        *fd = INVALID_FD;
      }
  }
  
--- 997,1015 ----
  }
  
      static void
! ch_close_part(channel_T *channel, ch_part_T part)
  {
+     sock_T *fd = &channel->ch_part[part].ch_fd;
+ 
      if (*fd != INVALID_FD)
      {
!       if (part == PART_SOCK)
!           sock_close(*fd);
!       else
!           fd_close(*fd);
        *fd = INVALID_FD;
+ 
+       channel->ch_to_be_closed &= ~(1 << part);
      }
  }
  
***************
*** 1012,1018 ****
  {
      if (in != INVALID_FD)
      {
!       may_close_part(&channel->CH_IN_FD);
        channel->CH_IN_FD = in;
      }
      if (out != INVALID_FD)
--- 1018,1024 ----
  {
      if (in != INVALID_FD)
      {
!       ch_close_part(channel, PART_IN);
        channel->CH_IN_FD = in;
      }
      if (out != INVALID_FD)
***************
*** 1020,1027 ****
  # if defined(FEAT_GUI)
        channel_gui_unregister_one(channel, PART_OUT);
  # endif
!       may_close_part(&channel->CH_OUT_FD);
        channel->CH_OUT_FD = out;
  # if defined(FEAT_GUI)
        channel_gui_register_one(channel, PART_OUT);
  # endif
--- 1026,1034 ----
  # if defined(FEAT_GUI)
        channel_gui_unregister_one(channel, PART_OUT);
  # endif
!       ch_close_part(channel, PART_OUT);
        channel->CH_OUT_FD = out;
+       channel->ch_to_be_closed |= (1 << PART_OUT);
  # if defined(FEAT_GUI)
        channel_gui_register_one(channel, PART_OUT);
  # endif
***************
*** 1031,1038 ****
  # if defined(FEAT_GUI)
        channel_gui_unregister_one(channel, PART_ERR);
  # endif
!       may_close_part(&channel->CH_ERR_FD);
        channel->CH_ERR_FD = err;
  # if defined(FEAT_GUI)
        channel_gui_register_one(channel, PART_ERR);
  # endif
--- 1038,1046 ----
  # if defined(FEAT_GUI)
        channel_gui_unregister_one(channel, PART_ERR);
  # endif
!       ch_close_part(channel, PART_ERR);
        channel->CH_ERR_FD = err;
+       channel->ch_to_be_closed |= (1 << PART_ERR);
  # if defined(FEAT_GUI)
        channel_gui_register_one(channel, PART_ERR);
  # endif
***************
*** 1151,1160 ****
      void
  channel_set_options(channel_T *channel, jobopt_T *opt)
  {
!     int               part;
  
      if (opt->jo_set & JO_MODE)
!       for (part = PART_SOCK; part <= PART_IN; ++part)
            channel->ch_part[part].ch_mode = opt->jo_mode;
      if (opt->jo_set & JO_IN_MODE)
        channel->ch_part[PART_IN].ch_mode = opt->jo_in_mode;
--- 1159,1168 ----
      void
  channel_set_options(channel_T *channel, jobopt_T *opt)
  {
!     ch_part_T part;
  
      if (opt->jo_set & JO_MODE)
!       for (part = PART_SOCK; part < PART_COUNT; ++part)
            channel->ch_part[part].ch_mode = opt->jo_mode;
      if (opt->jo_set & JO_IN_MODE)
        channel->ch_part[PART_IN].ch_mode = opt->jo_in_mode;
***************
*** 1164,1170 ****
        channel->ch_part[PART_ERR].ch_mode = opt->jo_err_mode;
  
      if (opt->jo_set & JO_TIMEOUT)
!       for (part = PART_SOCK; part <= PART_IN; ++part)
            channel->ch_part[part].ch_timeout = opt->jo_timeout;
      if (opt->jo_set & JO_OUT_TIMEOUT)
        channel->ch_part[PART_OUT].ch_timeout = opt->jo_out_timeout;
--- 1172,1178 ----
        channel->ch_part[PART_ERR].ch_mode = opt->jo_err_mode;
  
      if (opt->jo_set & JO_TIMEOUT)
!       for (part = PART_SOCK; part < PART_COUNT; ++part)
            channel->ch_part[part].ch_timeout = opt->jo_timeout;
      if (opt->jo_set & JO_OUT_TIMEOUT)
        channel->ch_part[PART_OUT].ch_timeout = opt->jo_out_timeout;
***************
*** 1282,1288 ****
      void
  channel_set_req_callback(
        channel_T   *channel,
!       int         part,
        char_u      *callback,
        partial_T   *partial,
        int         id)
--- 1290,1296 ----
      void
  channel_set_req_callback(
        channel_T   *channel,
!       ch_part_T   part,
        char_u      *callback,
        partial_T   *partial,
        int         id)
***************
*** 1448,1454 ****
        ch_log(channel, "Finished writing all lines to channel");
  
        /* Close the pipe/socket, so that the other side gets EOF. */
!       may_close_part(&channel->CH_IN_FD);
      }
      else
        ch_logn(channel, "Still %d more lines to write",
--- 1456,1462 ----
        ch_log(channel, "Finished writing all lines to channel");
  
        /* Close the pipe/socket, so that the other side gets EOF. */
!       ch_close_part(channel, PART_IN);
      }
      else
        ch_logn(channel, "Still %d more lines to write",
***************
*** 1462,1471 ****
  channel_buffer_free(buf_T *buf)
  {
      channel_T *channel;
!     int               part;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
!       for (part = PART_SOCK; part <= PART_IN; ++part)
        {
            chanpart_T  *ch_part = &channel->ch_part[part];
  
--- 1470,1479 ----
  channel_buffer_free(buf_T *buf)
  {
      channel_T *channel;
!     ch_part_T part;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
!       for (part = PART_SOCK; part < PART_COUNT; ++part)
        {
            chanpart_T  *ch_part = &channel->ch_part[part];
  
***************
*** 1574,1580 ****
   * Returns NULL if there is nothing.
   */
      readq_T *
! channel_peek(channel_T *channel, int part)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
  
--- 1582,1588 ----
   * Returns NULL if there is nothing.
   */
      readq_T *
! channel_peek(channel_T *channel, ch_part_T part)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
  
***************
*** 1604,1610 ****
   * Returns NULL if there is nothing.
   */
      char_u *
! channel_get(channel_T *channel, int part)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
      readq_T *node = head->rq_next;
--- 1612,1618 ----
   * Returns NULL if there is nothing.
   */
      char_u *
! channel_get(channel_T *channel, ch_part_T part)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
      readq_T *node = head->rq_next;
***************
*** 1628,1634 ****
   * Replaces NUL bytes with NL.
   */
      static char_u *
! channel_get_all(channel_T *channel, int part)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
      readq_T *node = head->rq_next;
--- 1636,1642 ----
   * Replaces NUL bytes with NL.
   */
      static char_u *
! channel_get_all(channel_T *channel, ch_part_T part)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
      readq_T *node = head->rq_next;
***************
*** 1677,1683 ****
   * Caller must check these bytes are available.
   */
      void
! channel_consume(channel_T *channel, int part, int len)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
      readq_T *node = head->rq_next;
--- 1685,1691 ----
   * Caller must check these bytes are available.
   */
      void
! channel_consume(channel_T *channel, ch_part_T part, int len)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
      readq_T *node = head->rq_next;
***************
*** 1693,1699 ****
   * When "want_nl" is TRUE collapse more buffers until a NL is found.
   */
      int
! channel_collapse(channel_T *channel, int part, int want_nl)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
      readq_T *node = head->rq_next;
--- 1701,1707 ----
   * When "want_nl" is TRUE collapse more buffers until a NL is found.
   */
      int
! channel_collapse(channel_T *channel, ch_part_T part, int want_nl)
  {
      readq_T *head = &channel->ch_part[part].ch_head;
      readq_T *node = head->rq_next;
***************
*** 1753,1759 ****
   * Returns OK or FAIL.
   */
      static int
! channel_save(channel_T *channel, int part, char_u *buf, int len,
                                                      int prepend, char *lead)
  {
      readq_T *node;
--- 1761,1767 ----
   * Returns OK or FAIL.
   */
      static int
! channel_save(channel_T *channel, ch_part_T part, char_u *buf, int len,
                                                      int prepend, char *lead)
  {
      readq_T *node;
***************
*** 1828,1834 ****
  channel_fill(js_read_T *reader)
  {
      channel_T *channel = (channel_T *)reader->js_cookie;
!     int               part = reader->js_cookie_arg;
      char_u    *next = channel_get(channel, part);
      int               unused;
      int               len;
--- 1836,1842 ----
  channel_fill(js_read_T *reader)
  {
      channel_T *channel = (channel_T *)reader->js_cookie;
!     ch_part_T part = reader->js_cookie_arg;
      char_u    *next = channel_get(channel, part);
      int               unused;
      int               len;
***************
*** 1866,1872 ****
   * Return TRUE if there is more to read.
   */
      static int
! channel_parse_json(channel_T *channel, int part)
  {
      js_read_T reader;
      typval_T  listtv;
--- 1874,1880 ----
   * Return TRUE if there is more to read.
   */
      static int
! channel_parse_json(channel_T *channel, ch_part_T part)
  {
      js_read_T reader;
      typval_T  listtv;
***************
*** 2046,2052 ****
   * Return FAIL otherwise.
   */
      static int
! channel_get_json(channel_T *channel, int part, int id, typval_T **rettv)
  {
      jsonq_T   *head = &channel->ch_part[part].ch_json_head;
      jsonq_T   *item = head->jq_next;
--- 2054,2060 ----
   * Return FAIL otherwise.
   */
      static int
! channel_get_json(channel_T *channel, ch_part_T part, int id, typval_T **rettv)
  {
      jsonq_T   *head = &channel->ch_part[part].ch_json_head;
      jsonq_T   *item = head->jq_next;
***************
*** 2080,2086 ****
   * "argv[1]" etc. have further arguments, type is VAR_UNKNOWN if missing.
   */
      static void
! channel_exe_cmd(channel_T *channel, int part, typval_T *argv)
  {
      char_u  *cmd = argv[0].vval.v_string;
      char_u  *arg;
--- 2088,2094 ----
   * "argv[1]" etc. have further arguments, type is VAR_UNKNOWN if missing.
   */
      static void
! channel_exe_cmd(channel_T *channel, ch_part_T part, typval_T *argv)
  {
      char_u  *cmd = argv[0].vval.v_string;
      char_u  *arg;
***************
*** 2237,2243 ****
  }
  
      static void
! append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, int part)
  {
      buf_T     *save_curbuf = curbuf;
      linenr_T    lnum = buffer->b_ml.ml_line_count;
--- 2245,2251 ----
  }
  
      static void
! append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T 
part)
  {
      buf_T     *save_curbuf = curbuf;
      linenr_T    lnum = buffer->b_ml.ml_line_count;
***************
*** 2332,2338 ****
  }
  
      static void
! drop_messages(channel_T *channel, int part)
  {
      char_u *msg;
  
--- 2340,2346 ----
  }
  
      static void
! drop_messages(channel_T *channel, ch_part_T part)
  {
      char_u *msg;
  
***************
*** 2349,2355 ****
   * Return TRUE when a message was handled, there might be another one.
   */
      static int
! may_invoke_callback(channel_T *channel, int part)
  {
      char_u    *msg = NULL;
      typval_T  *listtv = NULL;
--- 2357,2363 ----
   * Return TRUE when a message was handled, there might be another one.
   */
      static int
! may_invoke_callback(channel_T *channel, ch_part_T part)
  {
      char_u    *msg = NULL;
      typval_T  *listtv = NULL;
***************
*** 2596,2602 ****
   * Return TRUE if "channel" has JSON or other typeahead.
   */
      static int
! channel_has_readahead(channel_T *channel, int part)
  {
      ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
  
--- 2604,2610 ----
   * Return TRUE if "channel" has JSON or other typeahead.
   */
      static int
! channel_has_readahead(channel_T *channel, ch_part_T part)
  {
      ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
  
***************
*** 2617,2623 ****
      char *
  channel_status(channel_T *channel, int req_part)
  {
!     int part;
      int has_readahead = FALSE;
  
      if (channel == NULL)
--- 2625,2631 ----
      char *
  channel_status(channel_T *channel, int req_part)
  {
!     ch_part_T part;
      int has_readahead = FALSE;
  
      if (channel == NULL)
***************
*** 2640,2646 ****
      {
        if (channel_is_open(channel))
            return "open";
!       for (part = PART_SOCK; part <= PART_ERR; ++part)
            if (channel_has_readahead(channel, part))
            {
                has_readahead = TRUE;
--- 2648,2654 ----
      {
        if (channel_is_open(channel))
            return "open";
!       for (part = PART_SOCK; part < PART_IN; ++part)
            if (channel_has_readahead(channel, part))
            {
                has_readahead = TRUE;
***************
*** 2654,2660 ****
  }
  
      static void
! channel_part_info(channel_T *channel, dict_T *dict, char *name, int part)
  {
      chanpart_T *chanpart = &channel->ch_part[part];
      char      namebuf[20];  /* longest is "sock_timeout" */
--- 2662,2668 ----
  }
  
      static void
! channel_part_info(channel_T *channel, dict_T *dict, char *name, ch_part_T 
part)
  {
      chanpart_T *chanpart = &channel->ch_part[part];
      char      namebuf[20];  /* longest is "sock_timeout" */
***************
*** 2736,2763 ****
      channel_gui_unregister(channel);
  #endif
  
!     if (channel->CH_SOCK_FD != INVALID_FD)
!     {
!       sock_close(channel->CH_SOCK_FD);
!       channel->CH_SOCK_FD = INVALID_FD;
!     }
!     may_close_part(&channel->CH_IN_FD);
!     may_close_part(&channel->CH_OUT_FD);
!     may_close_part(&channel->CH_ERR_FD);
  
      if (invoke_close_cb && channel->ch_close_cb != NULL)
      {
          typval_T      argv[1];
          typval_T      rettv;
          int           dummy;
!         int           part;
  
          /* Invoke callbacks before the close callback, since it's weird to
           * first invoke the close callback.  Increment the refcount to avoid
           * the channel being freed halfway. */
          ++channel->ch_refcount;
          ch_log(channel, "Invoking callbacks before closing");
!         for (part = PART_SOCK; part <= PART_ERR; ++part)
              while (may_invoke_callback(channel, part))
                  ;
  
--- 2744,2767 ----
      channel_gui_unregister(channel);
  #endif
  
!     ch_close_part(channel, PART_SOCK);
!     ch_close_part(channel, PART_IN);
!     ch_close_part(channel, PART_OUT);
!     ch_close_part(channel, PART_ERR);
  
      if (invoke_close_cb && channel->ch_close_cb != NULL)
      {
          typval_T      argv[1];
          typval_T      rettv;
          int           dummy;
!         ch_part_T     part;
  
          /* Invoke callbacks before the close callback, since it's weird to
           * first invoke the close callback.  Increment the refcount to avoid
           * the channel being freed halfway. */
          ++channel->ch_refcount;
          ch_log(channel, "Invoking callbacks before closing");
!         for (part = PART_SOCK; part < PART_IN; ++part)
              while (may_invoke_callback(channel, part))
                  ;
  
***************
*** 2789,2795 ****
          }
  
          /* any remaining messages are useless now */
!         for (part = PART_SOCK; part <= PART_ERR; ++part)
              drop_messages(channel, part);
      }
  
--- 2793,2799 ----
          }
  
          /* any remaining messages are useless now */
!         for (part = PART_SOCK; part < PART_IN; ++part)
              drop_messages(channel, part);
      }
  
***************
*** 2802,2815 ****
      void
  channel_close_in(channel_T *channel)
  {
!     may_close_part(&channel->CH_IN_FD);
  }
  
  /*
   * Clear the read buffer on "channel"/"part".
   */
      static void
! channel_clear_one(channel_T *channel, int part)
  {
      jsonq_T *json_head = &channel->ch_part[part].ch_json_head;
      cbq_T   *cb_head = &channel->ch_part[part].ch_cb_head;
--- 2806,2819 ----
      void
  channel_close_in(channel_T *channel)
  {
!     ch_close_part(channel, PART_IN);
  }
  
  /*
   * Clear the read buffer on "channel"/"part".
   */
      static void
! channel_clear_one(channel_T *channel, ch_part_T part)
  {
      jsonq_T *json_head = &channel->ch_part[part].ch_json_head;
      cbq_T   *cb_head = &channel->ch_part[part].ch_cb_head;
***************
*** 3043,3053 ****
  }
  
      static void
! channel_close_on_error(channel_T *channel, char *func)
  {
!     /* Do not call emsg(), most likely the other end just exited. */
!     ch_errors(channel, "%s(): Cannot read from channel, will close it soon",
!                                                                       func);
  
      /* Queue a "DETACH" netbeans message in the command queue in order to
       * terminate the netbeans session later. Do not end the session here
--- 3047,3066 ----
  }
  
      static void
! ch_close_part_on_error(
!       channel_T *channel, ch_part_T part, int is_err, char *func)
  {
!     char      msgbuf[80];
! 
!     vim_snprintf(msgbuf, sizeof(msgbuf),
!           "%%s(): Read %s from ch_part[%d], closing",
!                                           (is_err ? "error" : "EOF"), part);
! 
!     if (is_err)
!       /* Do not call emsg(), most likely the other end just exited. */
!       ch_errors(channel, msgbuf, func);
!     else
!       ch_logs(channel, msgbuf, func);
  
      /* Queue a "DETACH" netbeans message in the command queue in order to
       * terminate the netbeans session later. Do not end the session here
***************
*** 3064,3084 ****
        channel_save(channel, PART_SOCK, (char_u *)DETACH_MSG_RAW,
                              (int)STRLEN(DETACH_MSG_RAW), FALSE, "PUT ");
  
!     /* When reading from stdout is not possible, assume the other side has
!      * died.  Don't close the channel right away, it may be the wrong moment
!      * to invoke callbacks. */
!     channel->ch_to_be_closed = TRUE;
  
  #ifdef FEAT_GUI
      /* Stop listening to GUI events right away. */
!     channel_gui_unregister(channel);
  #endif
  }
  
      static void
  channel_close_now(channel_T *channel)
  {
!     ch_log(channel, "Closing channel because of previous read error");
      channel_close(channel, TRUE);
      if (channel->ch_nb_close_cb != NULL)
        (*channel->ch_nb_close_cb)();
--- 3077,3096 ----
        channel_save(channel, PART_SOCK, (char_u *)DETACH_MSG_RAW,
                              (int)STRLEN(DETACH_MSG_RAW), FALSE, "PUT ");
  
!     /* When reading is not possible close this part of the channel.  Don't
!      * close the channel yet, there may be something to read on another part. 
*/
!     ch_close_part(channel, part);
  
  #ifdef FEAT_GUI
      /* Stop listening to GUI events right away. */
!     channel_gui_unregister_one(channel, part);
  #endif
  }
  
      static void
  channel_close_now(channel_T *channel)
  {
!     ch_log(channel, "Closing channel because all readable fds are closed");
      channel_close(channel, TRUE);
      if (channel->ch_nb_close_cb != NULL)
        (*channel->ch_nb_close_cb)();
***************
*** 3090,3096 ****
   * The data is put in the read queue.  No callbacks are invoked here.
   */
      static void
! channel_read(channel_T *channel, int part, char *func)
  {
      static char_u     *buf = NULL;
      int                       len = 0;
--- 3102,3108 ----
   * The data is put in the read queue.  No callbacks are invoked here.
   */
      static void
! channel_read(channel_T *channel, ch_part_T part, char *func)
  {
      static char_u     *buf = NULL;
      int                       len = 0;
***************
*** 3098,3111 ****
      sock_T            fd;
      int                       use_socket = FALSE;
  
-     /* If we detected a read error don't try reading again. */
-     if (channel->ch_to_be_closed)
-       return;
- 
      fd = channel->ch_part[part].ch_fd;
      if (fd == INVALID_FD)
      {
!       ch_error(channel, "channel_read() called while socket is closed");
        return;
      }
      use_socket = fd == channel->CH_SOCK_FD;
--- 3110,3120 ----
      sock_T            fd;
      int                       use_socket = FALSE;
  
      fd = channel->ch_part[part].ch_fd;
      if (fd == INVALID_FD)
      {
!       ch_errors(channel, "channel_read() called while %s part is closed",
!                                                           part_names[part]);
        return;
      }
      use_socket = fd == channel->CH_SOCK_FD;
***************
*** 3141,3147 ****
  
      /* Reading a disconnection (readlen == 0), or an error. */
      if (readlen <= 0)
!       channel_close_on_error(channel, func);
  
  #if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
      /* signal the main loop that there is something to read */
--- 3150,3156 ----
  
      /* Reading a disconnection (readlen == 0), or an error. */
      if (readlen <= 0)
!       ch_close_part_on_error(channel, part, (len < 0), func);
  
  #if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
      /* signal the main loop that there is something to read */
***************
*** 3157,3163 ****
   * Returns NULL in case of error or timeout.
   */
      char_u *
! channel_read_block(channel_T *channel, int part, int timeout)
  {
      char_u    *buf;
      char_u    *msg;
--- 3166,3172 ----
   * Returns NULL in case of error or timeout.
   */
      char_u *
! channel_read_block(channel_T *channel, ch_part_T part, int timeout)
  {
      char_u    *buf;
      char_u    *msg;
***************
*** 3237,3243 ****
      int
  channel_read_json_block(
        channel_T   *channel,
!       int         part,
        int         timeout_arg,
        int         id,
        typval_T    **rettv)
--- 3246,3252 ----
      int
  channel_read_json_block(
        channel_T   *channel,
!       ch_part_T   part,
        int         timeout_arg,
        int         id,
        typval_T    **rettv)
***************
*** 3323,3329 ****
  common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
  {
      channel_T *channel;
!     int               part = -1;
      jobopt_T  opt;
      int               mode;
      int               timeout;
--- 3332,3338 ----
  common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
  {
      channel_T *channel;
!     ch_part_T part = PART_COUNT;
      jobopt_T  opt;
      int               mode;
      int               timeout;
***************
*** 3344,3350 ****
      channel = get_channel_arg(&argvars[0], TRUE, TRUE, part);
      if (channel != NULL)
      {
!       if (part < 0)
            part = channel_part_read(channel);
        mode = channel_get_mode(channel, part);
        timeout = channel_get_timeout(channel, part);
--- 3353,3359 ----
      channel = get_channel_arg(&argvars[0], TRUE, TRUE, part);
      if (channel != NULL)
      {
!       if (part == PART_COUNT)
            part = channel_part_read(channel);
        mode = channel_get_mode(channel, part);
        timeout = channel_get_timeout(channel, part);
***************
*** 3382,3391 ****
   * Returns NULL when the socket isn't found.
   */
      channel_T *
! channel_fd2channel(sock_T fd, int *partp)
  {
      channel_T *channel;
!     int               part;
  
      if (fd != INVALID_FD)
        for (channel = first_channel; channel != NULL;
--- 3391,3400 ----
   * Returns NULL when the socket isn't found.
   */
      channel_T *
! channel_fd2channel(sock_T fd, ch_part_T *partp)
  {
      channel_T *channel;
!     ch_part_T part;
  
      if (fd != INVALID_FD)
        for (channel = first_channel; channel != NULL;
***************
*** 3411,3427 ****
  channel_handle_events(void)
  {
      channel_T *channel;
!     int               part;
      sock_T    fd;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
      {
-       /* If we detected a read error don't try reading again. */
-       if (channel->ch_to_be_closed)
-           continue;
- 
        /* check the socket and pipes */
!       for (part = PART_SOCK; part <= PART_ERR; ++part)
        {
            fd = channel->ch_part[part].ch_fd;
            if (fd != INVALID_FD)
--- 3420,3432 ----
  channel_handle_events(void)
  {
      channel_T *channel;
!     ch_part_T part;
      sock_T    fd;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
      {
        /* check the socket and pipes */
!       for (part = PART_SOCK; part < PART_IN; ++part)
        {
            fd = channel->ch_part[part].ch_fd;
            if (fd != INVALID_FD)
***************
*** 3431,3437 ****
                if (r == CW_READY)
                    channel_read(channel, part, "channel_handle_events");
                else if (r == CW_ERROR)
!                   channel_close_on_error(channel, "channel_handle_events()");
            }
        }
      }
--- 3436,3443 ----
                if (r == CW_READY)
                    channel_read(channel, part, "channel_handle_events");
                else if (r == CW_ERROR)
!                   ch_close_part_on_error(channel, part, TRUE,
!                                                    "channel_handle_events");
            }
        }
      }
***************
*** 3444,3450 ****
   * Return FAIL or OK.
   */
      int
! channel_send(channel_T *channel, int part, char_u *buf, int len, char *fun)
  {
      int               res;
      sock_T    fd;
--- 3450,3456 ----
   * Return FAIL or OK.
   */
      int
! channel_send(channel_T *channel, ch_part_T part, char_u *buf, int len, char 
*fun)
  {
      int               res;
      sock_T    fd;
***************
*** 3496,3502 ****
   * Sets "part_read" to the read fd.
   * Otherwise returns NULL.
   */
!     channel_T *
  send_common(
        typval_T    *argvars,
        char_u      *text,
--- 3502,3508 ----
   * Sets "part_read" to the read fd.
   * Otherwise returns NULL.
   */
!     static channel_T *
  send_common(
        typval_T    *argvars,
        char_u      *text,
***************
*** 3504,3513 ****
        int         eval,
        jobopt_T    *opt,
        char        *fun,
!       int         *part_read)
  {
      channel_T *channel;
!     int               part_send;
  
      clear_job_options(opt);
      channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0);
--- 3510,3519 ----
        int         eval,
        jobopt_T    *opt,
        char        *fun,
!       ch_part_T   *part_read)
  {
      channel_T *channel;
!     ch_part_T part_send;
  
      clear_job_options(opt);
      channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0);
***************
*** 3550,3557 ****
      channel_T *channel;
      int               id;
      ch_mode_T ch_mode;
!     int               part_send;
!     int               part_read;
      jobopt_T    opt;
      int               timeout;
  
--- 3556,3563 ----
      channel_T *channel;
      int               id;
      ch_mode_T ch_mode;
!     ch_part_T part_send;
!     ch_part_T part_read;
      jobopt_T    opt;
      int               timeout;
  
***************
*** 3610,3616 ****
      char_u    buf[NUMBUFLEN];
      char_u    *text;
      channel_T *channel;
!     int               part_read;
      jobopt_T    opt;
      int               timeout;
  
--- 3616,3622 ----
      char_u    buf[NUMBUFLEN];
      char_u    *text;
      channel_T *channel;
!     ch_part_T part_read;
      jobopt_T    opt;
      int               timeout;
  
***************
*** 3644,3650 ****
      int               nfd = nfd_in;
      channel_T *channel;
      struct    pollfd *fds = fds_in;
!     int               part;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
      {
--- 3650,3656 ----
      int               nfd = nfd_in;
      channel_T *channel;
      struct    pollfd *fds = fds_in;
!     ch_part_T part;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
      {
***************
*** 3678,3684 ****
      int               ret = ret_in;
      channel_T *channel;
      struct    pollfd *fds = fds_in;
!     int               part;
      int               idx;
      chanpart_T        *in_part;
  
--- 3684,3690 ----
      int               ret = ret_in;
      channel_T *channel;
      struct    pollfd *fds = fds_in;
!     ch_part_T part;
      int               idx;
      chanpart_T        *in_part;
  
***************
*** 3725,3731 ****
      channel_T *channel;
      fd_set    *rfds = rfds_in;
      fd_set    *wfds = wfds_in;
!     int               part;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
      {
--- 3731,3737 ----
      channel_T *channel;
      fd_set    *rfds = rfds_in;
      fd_set    *wfds = wfds_in;
!     ch_part_T part;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
      {
***************
*** 3757,3763 ****
      channel_T *channel;
      fd_set    *rfds = rfds_in;
      fd_set    *wfds = wfds_in;
!     int               part;
      chanpart_T        *in_part;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
--- 3763,3769 ----
      channel_T *channel;
      fd_set    *rfds = rfds_in;
      fd_set    *wfds = wfds_in;
!     ch_part_T part;
      chanpart_T        *in_part;
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
***************
*** 3803,3809 ****
      channel_T *channel = first_channel;
      int               ret = FALSE;
      int               r;
!     int               part = PART_SOCK;
  
      ++safe_to_invoke_callback;
  
--- 3809,3815 ----
      channel_T *channel = first_channel;
      int               ret = FALSE;
      int               r;
!     ch_part_T part = PART_SOCK;
  
      ++safe_to_invoke_callback;
  
***************
*** 3816,3824 ****
      }
      while (channel != NULL)
      {
!       if (channel->ch_to_be_closed)
        {
!           channel->ch_to_be_closed = FALSE;
            channel_close_now(channel);
            /* channel may have been freed, start over */
            channel = first_channel;
--- 3822,3830 ----
      }
      while (channel != NULL)
      {
!       if (channel->ch_to_be_closed == 0)
        {
!           channel->ch_to_be_closed = (1 << PART_COUNT);
            channel_close_now(channel);
            /* channel may have been freed, start over */
            channel = first_channel;
***************
*** 3840,3846 ****
            continue;
        }
        if (channel->ch_part[part].ch_fd != INVALID_FD
!               || channel_has_readahead(channel, part))
        {
            /* Increase the refcount, in case the handler causes the channel
             * to be unreferenced or closed. */
--- 3846,3852 ----
            continue;
        }
        if (channel->ch_part[part].ch_fd != INVALID_FD
!                                     || channel_has_readahead(channel, part))
        {
            /* Increase the refcount, in case the handler causes the channel
             * to be unreferenced or closed. */
***************
*** 3899,3905 ****
  /*
   * Return the "part" to write to for "channel".
   */
!     int
  channel_part_send(channel_T *channel)
  {
      if (channel->CH_SOCK_FD == INVALID_FD)
--- 3905,3911 ----
  /*
   * Return the "part" to write to for "channel".
   */
!     ch_part_T
  channel_part_send(channel_T *channel)
  {
      if (channel->CH_SOCK_FD == INVALID_FD)
***************
*** 3910,3916 ****
  /*
   * Return the default "part" to read from for "channel".
   */
!     int
  channel_part_read(channel_T *channel)
  {
      if (channel->CH_SOCK_FD == INVALID_FD)
--- 3916,3922 ----
  /*
   * Return the default "part" to read from for "channel".
   */
!     ch_part_T
  channel_part_read(channel_T *channel)
  {
      if (channel->CH_SOCK_FD == INVALID_FD)
***************
*** 3923,3929 ****
   * If "channel" is invalid returns MODE_JSON.
   */
      ch_mode_T
! channel_get_mode(channel_T *channel, int part)
  {
      if (channel == NULL)
        return MODE_JSON;
--- 3929,3935 ----
   * If "channel" is invalid returns MODE_JSON.
   */
      ch_mode_T
! channel_get_mode(channel_T *channel, ch_part_T part)
  {
      if (channel == NULL)
        return MODE_JSON;
***************
*** 3934,3940 ****
   * Return the timeout of "channel"/"part"
   */
      int
! channel_get_timeout(channel_T *channel, int part)
  {
      return channel->ch_part[part].ch_timeout;
  }
--- 3940,3946 ----
   * Return the timeout of "channel"/"part"
   */
      int
! channel_get_timeout(channel_T *channel, ch_part_T part)
  {
      return channel->ch_part[part].ch_timeout;
  }
***************
*** 3962,3968 ****
  }
  
      static int
! handle_io(typval_T *item, int part, jobopt_T *opt)
  {
      char_u    *val = get_tv_string(item);
  
--- 3968,3974 ----
  }
  
      static int
! handle_io(typval_T *item, ch_part_T part, jobopt_T *opt)
  {
      char_u    *val = get_tv_string(item);
  
***************
*** 4045,4051 ****
      dict_T    *dict;
      int               todo;
      hashitem_T        *hi;
!     int               part;
  
      opt->jo_set = 0;
      if (tv->v_type == VAR_UNKNOWN)
--- 4051,4057 ----
      dict_T    *dict;
      int               todo;
      hashitem_T        *hi;
!     ch_part_T part;
  
      opt->jo_set = 0;
      if (tv->v_type == VAR_UNKNOWN)
***************
*** 4343,4352 ****
   * Returns NULL if the handle is invalid.
   * When "check_open" is TRUE check that the channel can be used.
   * When "reading" is TRUE "check_open" considers typeahead useful.
!  * "part" is used to check typeahead, when -1 use the default part.
   */
      channel_T *
! get_channel_arg(typval_T *tv, int check_open, int reading, int part)
  {
      channel_T *channel = NULL;
      int               has_readahead = FALSE;
--- 4349,4358 ----
   * Returns NULL if the handle is invalid.
   * When "check_open" is TRUE check that the channel can be used.
   * When "reading" is TRUE "check_open" considers typeahead useful.
!  * "part" is used to check typeahead, when PART_COUNT use the default part.
   */
      channel_T *
! get_channel_arg(typval_T *tv, int check_open, int reading, ch_part_T part)
  {
      channel_T *channel = NULL;
      int               has_readahead = FALSE;
***************
*** 4367,4373 ****
      }
      if (channel != NULL && reading)
        has_readahead = channel_has_readahead(channel,
!                              part >= 0 ? part : channel_part_read(channel));
  
      if (check_open && (channel == NULL || (!channel_is_open(channel)
                                             && !(reading && has_readahead))))
--- 4373,4379 ----
      }
      if (channel != NULL && reading)
        has_readahead = channel_has_readahead(channel,
!                      part != PART_COUNT ? part : channel_part_read(channel));
  
      if (check_open && (channel == NULL || (!channel_is_open(channel)
                                             && !(reading && has_readahead))))
***************
*** 4659,4665 ****
      garray_T  ga;
  #endif
      jobopt_T  opt;
!     int               part;
  
      job = job_alloc();
      if (job == NULL)
--- 4665,4671 ----
      garray_T  ga;
  #endif
      jobopt_T  opt;
!     ch_part_T part;
  
      job = job_alloc();
      if (job == NULL)
***************
*** 4679,4685 ****
        goto theend;
  
      /* Check that when io is "file" that there is a file name. */
!     for (part = PART_OUT; part <= PART_IN; ++part)
        if ((opt.jo_set & (JO_OUT_IO << (part - PART_OUT)))
                && opt.jo_io[part] == JIO_FILE
                && (!(opt.jo_set & (JO_OUT_NAME << (part - PART_OUT)))
--- 4685,4691 ----
        goto theend;
  
      /* Check that when io is "file" that there is a file name. */
!     for (part = PART_OUT; part < PART_COUNT; ++part)
        if ((opt.jo_set & (JO_OUT_IO << (part - PART_OUT)))
                && opt.jo_io[part] == JIO_FILE
                && (!(opt.jo_set & (JO_OUT_NAME << (part - PART_OUT)))
*** ../vim-8.0.0026/src/eval.c  2016-09-26 22:58:54.498255420 +0200
--- src/eval.c  2016-10-09 16:54:57.401187760 +0200
***************
*** 5622,5628 ****
      else if (tv->v_type == VAR_CHANNEL)
      {
        channel_T   *ch =tv->vval.v_channel;
!       int         part;
        typval_T    dtv;
        jsonq_T     *jq;
        cbq_T       *cq;
--- 5622,5628 ----
      else if (tv->v_type == VAR_CHANNEL)
      {
        channel_T   *ch =tv->vval.v_channel;
!       ch_part_T   part;
        typval_T    dtv;
        jsonq_T     *jq;
        cbq_T       *cq;
***************
*** 5630,5636 ****
        if (ch != NULL && ch->ch_copyID != copyID)
        {
            ch->ch_copyID = copyID;
!           for (part = PART_SOCK; part <= PART_IN; ++part)
            {
                for (jq = ch->ch_part[part].ch_json_head.jq_next; jq != NULL;
                                                             jq = jq->jq_next)
--- 5630,5636 ----
        if (ch != NULL && ch->ch_copyID != copyID)
        {
            ch->ch_copyID = copyID;
!           for (part = PART_SOCK; part < PART_COUNT; ++part)
            {
                for (jq = ch->ch_part[part].ch_json_head.jq_next; jq != NULL;
                                                             jq = jq->jq_next)
*** ../vim-8.0.0026/src/structs.h       2016-09-07 22:48:11.000000000 +0200
--- src/structs.h       2016-10-09 16:39:57.571448152 +0200
***************
*** 1499,1517 ****
  
  /* Ordering matters, it is used in for loops: IN is last, only SOCK/OUT/ERR
   * are polled. */
! #define PART_SOCK   0
  #define CH_SOCK_FD    ch_part[PART_SOCK].ch_fd
- 
  #ifdef FEAT_JOB_CHANNEL
! # define INVALID_FD  (-1)
! 
! # define PART_OUT   1
! # define PART_ERR   2
! # define PART_IN    3
  # define CH_OUT_FD    ch_part[PART_OUT].ch_fd
  # define CH_ERR_FD    ch_part[PART_ERR].ch_fd
  # define CH_IN_FD     ch_part[PART_IN].ch_fd
  #endif
  
  /* The per-fd info for a channel. */
  typedef struct {
--- 1499,1519 ----
  
  /* Ordering matters, it is used in for loops: IN is last, only SOCK/OUT/ERR
   * are polled. */
! typedef enum {
!     PART_SOCK = 0,
  #define CH_SOCK_FD    ch_part[PART_SOCK].ch_fd
  #ifdef FEAT_JOB_CHANNEL
!     PART_OUT,
  # define CH_OUT_FD    ch_part[PART_OUT].ch_fd
+     PART_ERR,
  # define CH_ERR_FD    ch_part[PART_ERR].ch_fd
+     PART_IN,
  # define CH_IN_FD     ch_part[PART_IN].ch_fd
  #endif
+     PART_COUNT
+ } ch_part_T;
+ 
+ #define INVALID_FD    (-1)
  
  /* The per-fd info for a channel. */
  typedef struct {
***************
*** 1566,1579 ****
      int               ch_id;          /* ID of the channel */
      int               ch_last_msg_id; /* ID of the last message */
  
!     chanpart_T        ch_part[4];     /* info for socket, out, err and in */
  
      char      *ch_hostname;   /* only for socket, allocated */
      int               ch_port;        /* only for socket */
  
!     int               ch_to_be_closed; /* When TRUE reading or writing failed 
and
!                                 * the channel must be closed when it's safe
!                                 * to invoke callbacks. */
      int               ch_to_be_freed; /* When TRUE channel must be freed when 
it's
                                 * safe to invoke callbacks. */
      int               ch_error;       /* When TRUE an error was reported.  
Avoids
--- 1568,1581 ----
      int               ch_id;          /* ID of the channel */
      int               ch_last_msg_id; /* ID of the last message */
  
!     chanpart_T        ch_part[PART_COUNT]; /* info for socket, out, err and 
in */
  
      char      *ch_hostname;   /* only for socket, allocated */
      int               ch_port;        /* only for socket */
  
!     int               ch_to_be_closed; /* bitset of readable fds to be closed.
!                                 * When all readable fds have been closed,
!                                 * set to (1 << PART_COUNT). */
      int               ch_to_be_freed; /* When TRUE channel must be freed when 
it's
                                 * safe to invoke callbacks. */
      int               ch_error;       /* When TRUE an error was reported.  
Avoids
*** ../vim-8.0.0026/src/proto/channel.pro       2016-09-29 15:18:51.351768068 
+0200
--- src/proto/channel.pro       2016-10-09 17:00:24.546924836 +0200
***************
*** 14,28 ****
  void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err);
  void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options);
  void channel_set_options(channel_T *channel, jobopt_T *opt);
! void channel_set_req_callback(channel_T *channel, int part, char_u *callback, 
partial_T *partial, int id);
  void channel_buffer_free(buf_T *buf);
  void channel_write_any_lines(void);
  void channel_write_new_lines(buf_T *buf);
! readq_T *channel_peek(channel_T *channel, int part);
  char_u *channel_first_nl(readq_T *node);
! char_u *channel_get(channel_T *channel, int part);
! void channel_consume(channel_T *channel, int part, int len);
! int channel_collapse(channel_T *channel, int part, int want_nl);
  int channel_can_write_to(channel_T *channel);
  int channel_is_open(channel_T *channel);
  char *channel_status(channel_T *channel, int req_part);
--- 14,28 ----
  void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err);
  void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options);
  void channel_set_options(channel_T *channel, jobopt_T *opt);
! void channel_set_req_callback(channel_T *channel, ch_part_T part, char_u 
*callback, partial_T *partial, int id);
  void channel_buffer_free(buf_T *buf);
  void channel_write_any_lines(void);
  void channel_write_new_lines(buf_T *buf);
! readq_T *channel_peek(channel_T *channel, ch_part_T part);
  char_u *channel_first_nl(readq_T *node);
! char_u *channel_get(channel_T *channel, ch_part_T part);
! void channel_consume(channel_T *channel, ch_part_T part, int len);
! int channel_collapse(channel_T *channel, ch_part_T part, int want_nl);
  int channel_can_write_to(channel_T *channel);
  int channel_is_open(channel_T *channel);
  char *channel_status(channel_T *channel, int req_part);
***************
*** 31,43 ****
  void channel_close_in(channel_T *channel);
  void channel_clear(channel_T *channel);
  void channel_free_all(void);
! char_u *channel_read_block(channel_T *channel, int part, int timeout);
! int channel_read_json_block(channel_T *channel, int part, int timeout_arg, 
int id, typval_T **rettv);
  void common_channel_read(typval_T *argvars, typval_T *rettv, int raw);
! channel_T *channel_fd2channel(sock_T fd, int *partp);
  void channel_handle_events(void);
! int channel_send(channel_T *channel, int part, char_u *buf, int len, char 
*fun);
! channel_T *send_common(typval_T *argvars, char_u *text, int id, int eval, 
jobopt_T *opt, char *fun, int *part_read);
  void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval);
  void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval);
  int channel_poll_setup(int nfd_in, void *fds_in);
--- 31,42 ----
  void channel_close_in(channel_T *channel);
  void channel_clear(channel_T *channel);
  void channel_free_all(void);
! char_u *channel_read_block(channel_T *channel, ch_part_T part, int timeout);
! int channel_read_json_block(channel_T *channel, ch_part_T part, int 
timeout_arg, int id, typval_T **rettv);
  void common_channel_read(typval_T *argvars, typval_T *rettv, int raw);
! channel_T *channel_fd2channel(sock_T fd, ch_part_T *partp);
  void channel_handle_events(void);
! int channel_send(channel_T *channel, ch_part_T part, char_u *buf, int len, 
char *fun);
  void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval);
  void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval);
  int channel_poll_setup(int nfd_in, void *fds_in);
***************
*** 46,59 ****
  int channel_select_check(int ret_in, void *rfds_in, void *wfds_in);
  int channel_parse_messages(void);
  int set_ref_in_channel(int copyID);
! int channel_part_send(channel_T *channel);
! int channel_part_read(channel_T *channel);
! ch_mode_T channel_get_mode(channel_T *channel, int part);
! int channel_get_timeout(channel_T *channel, int part);
  void clear_job_options(jobopt_T *opt);
  void free_job_options(jobopt_T *opt);
  int get_job_options(typval_T *tv, jobopt_T *opt, int supported);
! channel_T *get_channel_arg(typval_T *tv, int check_open, int reading, int 
part);
  void job_free_all(void);
  int set_ref_in_job(int copyID);
  void job_unref(job_T *job);
--- 45,58 ----
  int channel_select_check(int ret_in, void *rfds_in, void *wfds_in);
  int channel_parse_messages(void);
  int set_ref_in_channel(int copyID);
! ch_part_T channel_part_send(channel_T *channel);
! ch_part_T channel_part_read(channel_T *channel);
! ch_mode_T channel_get_mode(channel_T *channel, ch_part_T part);
! int channel_get_timeout(channel_T *channel, ch_part_T part);
  void clear_job_options(jobopt_T *opt);
  void free_job_options(jobopt_T *opt);
  int get_job_options(typval_T *tv, jobopt_T *opt, int supported);
! channel_T *get_channel_arg(typval_T *tv, int check_open, int reading, 
ch_part_T part);
  void job_free_all(void);
  int set_ref_in_job(int copyID);
  void job_unref(job_T *job);
*** ../vim-8.0.0026/src/testdir/test_channel.vim        2016-10-03 
21:37:37.619829811 +0200
--- src/testdir/test_channel.vim        2016-10-09 16:28:59.732061278 +0200
***************
*** 1505,1510 ****
--- 1505,1527 ----
    call assert_equal(3, g:linecount)
  endfunc
  
+ func Test_read_from_terminated_job()
+   if !has('job')
+     return
+   endif
+ 
+   let g:linecount = 0
+   if has('win32')
+     " workaround: 'shellescape' does improper escaping double quotes 
+     let arg = 'import os,sys;os.close(1);sys.stderr.write(\"test\n\")'
+   else
+     let arg = 'import os,sys;os.close(1);sys.stderr.write("test\n")'
+   endif
+   call job_start([s:python, '-c', arg], {'callback': 'MyLineCountCb'})
+   call WaitFor('1 <= g:linecount')
+   call assert_equal(1, g:linecount)
+ endfunc
+ 
  function Ch_test_close_lambda(port)
    let handle = ch_open('localhost:' . a:port, s:chopt)
    if ch_status(handle) == "fail"
*** ../vim-8.0.0026/src/version.c       2016-10-09 16:10:02.135942266 +0200
--- src/version.c       2016-10-09 17:01:36.282423622 +0200
***************
*** 766,767 ****
--- 766,769 ----
  {   /* Add new patch number below this line */
+ /**/
+     27,
  /**/

-- 
       [Autumn changed into Winter ... Winter changed into Spring ...  Spring
       changed back into Autumn and Autumn gave Winter and Spring a miss and
       went straight on into Summer ...  Until one day ...]
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [email protected] -- 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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui