To: vim_dev@googlegroups.com Subject: Patch 7.4.1789 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- 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 ///