Patch 7.4.1789
Problem:    Cannot use ch_read() in the close callback.
Solution:   Do not discard the channel if there is readahead.  Do not discard
            readahead if there is a close callback.
Files:      src/eval.c, src/channel.c, src/proto/channel.pro,
            src/testdir/test_channel.vim


*** ../vim-7.4.1788/src/eval.c  2016-04-23 15:30:00.542243189 +0200
--- src/eval.c  2016-04-26 18:36:18.189566285 +0200
***************
*** 10305,10311 ****
      static void
  f_ch_close(typval_T *argvars, typval_T *rettv UNUSED)
  {
!     channel_T *channel = get_channel_arg(&argvars[0], TRUE);
  
      if (channel != NULL)
      {
--- 10305,10311 ----
      static void
  f_ch_close(typval_T *argvars, typval_T *rettv UNUSED)
  {
!     channel_T *channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0);
  
      if (channel != NULL)
      {
***************
*** 10320,10326 ****
      static void
  f_ch_getbufnr(typval_T *argvars, typval_T *rettv)
  {
!     channel_T *channel = get_channel_arg(&argvars[0], TRUE);
  
      rettv->vval.v_number = -1;
      if (channel != NULL)
--- 10320,10326 ----
      static void
  f_ch_getbufnr(typval_T *argvars, typval_T *rettv)
  {
!     channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
  
      rettv->vval.v_number = -1;
      if (channel != NULL)
***************
*** 10347,10353 ****
      static void
  f_ch_getjob(typval_T *argvars, typval_T *rettv)
  {
!     channel_T *channel = get_channel_arg(&argvars[0], TRUE);
  
      if (channel != NULL)
      {
--- 10347,10353 ----
      static void
  f_ch_getjob(typval_T *argvars, typval_T *rettv)
  {
!     channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
  
      if (channel != NULL)
      {
***************
*** 10364,10370 ****
      static void
  f_ch_info(typval_T *argvars, typval_T *rettv UNUSED)
  {
!     channel_T *channel = get_channel_arg(&argvars[0], TRUE);
  
      if (channel != NULL && rettv_dict_alloc(rettv) != FAIL)
        channel_info(channel, rettv->vval.v_dict);
--- 10364,10370 ----
      static void
  f_ch_info(typval_T *argvars, typval_T *rettv UNUSED)
  {
!     channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
  
      if (channel != NULL && rettv_dict_alloc(rettv) != FAIL)
        channel_info(channel, rettv->vval.v_dict);
***************
*** 10380,10386 ****
      channel_T *channel = NULL;
  
      if (argvars[1].v_type != VAR_UNKNOWN)
!       channel = get_channel_arg(&argvars[1], TRUE);
  
      ch_log(channel, (char *)msg);
  }
--- 10380,10386 ----
      channel_T *channel = NULL;
  
      if (argvars[1].v_type != VAR_UNKNOWN)
!       channel = get_channel_arg(&argvars[1], FALSE, FALSE, 0);
  
      ch_log(channel, (char *)msg);
  }
***************
*** 10476,10482 ****
      channel_T *channel;
      jobopt_T  opt;
  
!     channel = get_channel_arg(&argvars[0], TRUE);
      if (channel == NULL)
        return;
      clear_job_options(&opt);
--- 10476,10482 ----
      channel_T *channel;
      jobopt_T  opt;
  
!     channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
      if (channel == NULL)
        return;
      clear_job_options(&opt);
***************
*** 10498,10504 ****
      rettv->v_type = VAR_STRING;
      rettv->vval.v_string = NULL;
  
!     channel = get_channel_arg(&argvars[0], FALSE);
      rettv->vval.v_string = vim_strsave((char_u *)channel_status(channel));
  }
  #endif
--- 10498,10504 ----
      rettv->v_type = VAR_STRING;
      rettv->vval.v_string = NULL;
  
!     channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
      rettv->vval.v_string = vim_strsave((char_u *)channel_status(channel));
  }
  #endif
*** ../vim-7.4.1788/src/channel.c       2016-04-26 17:16:13.671979146 +0200
--- src/channel.c       2016-04-26 18:53:04.726341642 +0200
***************
*** 2103,2108 ****
--- 2103,2120 ----
      }
  }
  
+     static void
+ drop_messages(channel_T *channel, int part)
+ {
+     char_u *msg;
+ 
+     while ((msg = channel_get(channel, part)) != NULL)
+     {
+       ch_logs(channel, "Dropping message '%s'", (char *)msg);
+       vim_free(msg);
+     }
+ }
+ 
  /*
   * Invoke a callback for "channel"/"part" if needed.
   * This does not redraw but sets channel_need_redraw when redraw is needed.
***************
*** 2202,2212 ****
        /* If there is no callback or buffer drop the message. */
        if (callback == NULL && buffer == NULL)
        {
!           while ((msg = channel_get(channel, part)) != NULL)
!           {
!               ch_logs(channel, "Dropping message '%s'", (char *)msg);
!               vim_free(msg);
!           }
            return FALSE;
        }
  
--- 2214,2223 ----
        /* If there is no callback or buffer drop the message. */
        if (callback == NULL && buffer == NULL)
        {
!           /* If there is a close callback it may use ch_read() to get the
!            * messages. */
!           if (channel->ch_close_cb == NULL)
!               drop_messages(channel, part);
            return FALSE;
        }
  
***************
*** 2326,2340 ****
--- 2337,2381 ----
  }
  
  /*
+  * 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;
+ 
+     if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
+     {
+       jsonq_T   *head = &channel->ch_part[part].ch_json_head;
+       jsonq_T   *item = head->jq_next;
+ 
+       return item != NULL;
+     }
+     return channel_peek(channel, part) != NULL;
+ }
+ 
+ /*
   * Return a string indicating the status of the channel.
   */
      char *
  channel_status(channel_T *channel)
  {
+     int part;
+     int has_readahead = FALSE;
+ 
      if (channel == NULL)
         return "fail";
      if (channel_is_open(channel))
         return "open";
+     for (part = PART_SOCK; part <= PART_ERR; ++part)
+       if (channel_has_readahead(channel, part))
+       {
+           has_readahead = TRUE;
+           break;
+       }
+ 
+     if (has_readahead)
+       return "buffered";
      return "closed";
  }
  
***************
*** 2458,2463 ****
--- 2499,2508 ----
          channel->ch_close_cb = NULL;
          partial_unref(channel->ch_close_partial);
          channel->ch_close_partial = NULL;
+ 
+         /* any remaining messages are useless now */
+         for (part = PART_SOCK; part <= PART_ERR; ++part)
+             drop_messages(channel, part);
      }
  
      channel->ch_nb_close_cb = NULL;
***************
*** 2967,2973 ****
  common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
  {
      channel_T *channel;
!     int               part;
      jobopt_T  opt;
      int               mode;
      int               timeout;
--- 3012,3018 ----
  common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
  {
      channel_T *channel;
!     int               part = -1;
      jobopt_T  opt;
      int               mode;
      int               timeout;
***************
*** 2983,2994 ****
                                                                      == FAIL)
        goto theend;
  
!     channel = get_channel_arg(&argvars[0], TRUE);
      if (channel != NULL)
      {
!       if (opt.jo_set & JO_PART)
!           part = opt.jo_part;
!       else
            part = channel_part_read(channel);
        mode = channel_get_mode(channel, part);
        timeout = channel_get_timeout(channel, part);
--- 3028,3039 ----
                                                                      == FAIL)
        goto theend;
  
!     if (opt.jo_set & JO_PART)
!       part = opt.jo_part;
!     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);
***************
*** 3152,3158 ****
      int               part_send;
  
      clear_job_options(opt);
!     channel = get_channel_arg(&argvars[0], TRUE);
      if (channel == NULL)
        return NULL;
      part_send = channel_part_send(channel);
--- 3197,3203 ----
      int               part_send;
  
      clear_job_options(opt);
!     channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0);
      if (channel == NULL)
        return NULL;
      part_send = channel_part_send(channel);
***************
*** 3201,3207 ****
      rettv->v_type = VAR_STRING;
      rettv->vval.v_string = NULL;
  
!     channel = get_channel_arg(&argvars[0], TRUE);
      if (channel == NULL)
        return;
      part_send = channel_part_send(channel);
--- 3246,3252 ----
      rettv->v_type = VAR_STRING;
      rettv->vval.v_string = NULL;
  
!     channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0);
      if (channel == NULL)
        return;
      part_send = channel_part_send(channel);
***************
*** 3435,3458 ****
  # endif /* !WIN32 && HAVE_SELECT */
  
  /*
-  * 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;
- 
-     if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
-     {
-       jsonq_T   *head = &channel->ch_part[part].ch_json_head;
-       jsonq_T   *item = head->jq_next;
- 
-       return item != NULL;
-     }
-     return channel_peek(channel, part) != NULL;
- }
- 
- /*
   * Execute queued up commands.
   * Invoked from the main loop when it's safe to execute received commands.
   * Return TRUE when something was done.
--- 3480,3485 ----
***************
*** 3968,3978 ****
  /*
   * Get the channel from the argument.
   * Returns NULL if the handle is invalid.
   */
      channel_T *
! get_channel_arg(typval_T *tv, int check_open)
  {
!     channel_T *channel = NULL;
  
      if (tv->v_type == VAR_JOB)
      {
--- 3995,4009 ----
  /*
   * Get the channel from the argument.
   * 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;
  
      if (tv->v_type == VAR_JOB)
      {
***************
*** 3988,3995 ****
        EMSG2(_(e_invarg2), get_tv_string(tv));
        return NULL;
      }
  
!     if (check_open && (channel == NULL || !channel_is_open(channel)))
      {
        EMSG(_("E906: not an open channel"));
        return NULL;
--- 4019,4030 ----
        EMSG2(_(e_invarg2), get_tv_string(tv));
        return NULL;
      }
+     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))))
      {
        EMSG(_("E906: not an open channel"));
        return NULL;
*** ../vim-7.4.1788/src/proto/channel.pro       2016-04-26 17:16:13.671979146 
+0200
--- src/proto/channel.pro       2016-04-26 18:36:21.613528131 +0200
***************
*** 48,54 ****
  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);
  void job_unref(job_T *job);
  int free_unused_jobs_contents(int copyID, int mask);
  void free_unused_jobs(int copyID, int mask);
--- 48,54 ----
  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_unref(job_T *job);
  int free_unused_jobs_contents(int copyID, int mask);
  void free_unused_jobs(int copyID, int mask);
*** ../vim-7.4.1788/src/testdir/test_channel.vim        2016-04-26 
17:16:13.675979102 +0200
--- src/testdir/test_channel.vim        2016-04-26 18:22:54.594530474 +0200
***************
*** 1080,1085 ****
--- 1080,1107 ----
    endtry
  endfunc
  
+ func Test_read_in_close_cb()
+   if !has('job')
+     return
+   endif
+   call ch_log('Test_read_in_close_cb()')
+ 
+   let s:received = ''
+   func! CloseHandler(chan)
+     let s:received = ch_read(a:chan)
+   endfunc
+   let job = job_start(s:python . " test_channel_pipe.py quit now",
+       \ {'close_cb': 'CloseHandler'})
+   call assert_equal("run", job_status(job))
+   try
+     call s:waitFor('s:received != ""')
+     call assert_equal('quit', s:received)
+   finally
+     call job_stop(job)
+     delfunc CloseHandler
+   endtry
+ endfunc
+ 
  """"""""""
  
  let s:unletResponse = ''
*** ../vim-7.4.1788/src/version.c       2016-04-26 17:34:40.731767572 +0200
--- src/version.c       2016-04-26 18:55:01.561037721 +0200
***************
*** 755,756 ****
--- 755,758 ----
  {   /* Add new patch number below this line */
+ /**/
+     1789,
  /**/

-- 
Vi beats Emacs to death, and then again!
                        http://linuxtoday.com/stories/5764.html

 /// 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