Skip to content

Commit 437905c

Browse files
committed
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.
1 parent c7baa43 commit 437905c

5 files changed

Lines changed: 100 additions & 41 deletions

File tree

src/channel.c

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,6 +2103,18 @@ append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel)
21032103
}
21042104
}
21052105

2106+
static void
2107+
drop_messages(channel_T *channel, int part)
2108+
{
2109+
char_u *msg;
2110+
2111+
while ((msg = channel_get(channel, part)) != NULL)
2112+
{
2113+
ch_logs(channel, "Dropping message '%s'", (char *)msg);
2114+
vim_free(msg);
2115+
}
2116+
}
2117+
21062118
/*
21072119
* Invoke a callback for "channel"/"part" if needed.
21082120
* This does not redraw but sets channel_need_redraw when redraw is needed.
@@ -2202,11 +2214,10 @@ may_invoke_callback(channel_T *channel, int part)
22022214
/* If there is no callback or buffer drop the message. */
22032215
if (callback == NULL && buffer == NULL)
22042216
{
2205-
while ((msg = channel_get(channel, part)) != NULL)
2206-
{
2207-
ch_logs(channel, "Dropping message '%s'", (char *)msg);
2208-
vim_free(msg);
2209-
}
2217+
/* If there is a close callback it may use ch_read() to get the
2218+
* messages. */
2219+
if (channel->ch_close_cb == NULL)
2220+
drop_messages(channel, part);
22102221
return FALSE;
22112222
}
22122223

@@ -2325,16 +2336,46 @@ channel_is_open(channel_T *channel)
23252336
|| channel->CH_ERR_FD != INVALID_FD);
23262337
}
23272338

2339+
/*
2340+
* Return TRUE if "channel" has JSON or other typeahead.
2341+
*/
2342+
static int
2343+
channel_has_readahead(channel_T *channel, int part)
2344+
{
2345+
ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
2346+
2347+
if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
2348+
{
2349+
jsonq_T *head = &channel->ch_part[part].ch_json_head;
2350+
jsonq_T *item = head->jq_next;
2351+
2352+
return item != NULL;
2353+
}
2354+
return channel_peek(channel, part) != NULL;
2355+
}
2356+
23282357
/*
23292358
* Return a string indicating the status of the channel.
23302359
*/
23312360
char *
23322361
channel_status(channel_T *channel)
23332362
{
2363+
int part;
2364+
int has_readahead = FALSE;
2365+
23342366
if (channel == NULL)
23352367
return "fail";
23362368
if (channel_is_open(channel))
23372369
return "open";
2370+
for (part = PART_SOCK; part <= PART_ERR; ++part)
2371+
if (channel_has_readahead(channel, part))
2372+
{
2373+
has_readahead = TRUE;
2374+
break;
2375+
}
2376+
2377+
if (has_readahead)
2378+
return "buffered";
23382379
return "closed";
23392380
}
23402381

@@ -2458,6 +2499,10 @@ channel_close(channel_T *channel, int invoke_close_cb)
24582499
channel->ch_close_cb = NULL;
24592500
partial_unref(channel->ch_close_partial);
24602501
channel->ch_close_partial = NULL;
2502+
2503+
/* any remaining messages are useless now */
2504+
for (part = PART_SOCK; part <= PART_ERR; ++part)
2505+
drop_messages(channel, part);
24612506
}
24622507

24632508
channel->ch_nb_close_cb = NULL;
@@ -2967,7 +3012,7 @@ channel_read_json_block(
29673012
common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
29683013
{
29693014
channel_T *channel;
2970-
int part;
3015+
int part = -1;
29713016
jobopt_T opt;
29723017
int mode;
29733018
int timeout;
@@ -2983,12 +3028,12 @@ common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
29833028
== FAIL)
29843029
goto theend;
29853030

2986-
channel = get_channel_arg(&argvars[0], TRUE);
3031+
if (opt.jo_set & JO_PART)
3032+
part = opt.jo_part;
3033+
channel = get_channel_arg(&argvars[0], TRUE, TRUE, part);
29873034
if (channel != NULL)
29883035
{
2989-
if (opt.jo_set & JO_PART)
2990-
part = opt.jo_part;
2991-
else
3036+
if (part < 0)
29923037
part = channel_part_read(channel);
29933038
mode = channel_get_mode(channel, part);
29943039
timeout = channel_get_timeout(channel, part);
@@ -3152,7 +3197,7 @@ send_common(
31523197
int part_send;
31533198

31543199
clear_job_options(opt);
3155-
channel = get_channel_arg(&argvars[0], TRUE);
3200+
channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0);
31563201
if (channel == NULL)
31573202
return NULL;
31583203
part_send = channel_part_send(channel);
@@ -3201,7 +3246,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
32013246
rettv->v_type = VAR_STRING;
32023247
rettv->vval.v_string = NULL;
32033248

3204-
channel = get_channel_arg(&argvars[0], TRUE);
3249+
channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0);
32053250
if (channel == NULL)
32063251
return;
32073252
part_send = channel_part_send(channel);
@@ -3434,24 +3479,6 @@ channel_select_check(int ret_in, void *rfds_in, void *wfds_in)
34343479
}
34353480
# endif /* !WIN32 && HAVE_SELECT */
34363481

3437-
/*
3438-
* Return TRUE if "channel" has JSON or other typeahead.
3439-
*/
3440-
static int
3441-
channel_has_readahead(channel_T *channel, int part)
3442-
{
3443-
ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
3444-
3445-
if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
3446-
{
3447-
jsonq_T *head = &channel->ch_part[part].ch_json_head;
3448-
jsonq_T *item = head->jq_next;
3449-
3450-
return item != NULL;
3451-
}
3452-
return channel_peek(channel, part) != NULL;
3453-
}
3454-
34553482
/*
34563483
* Execute queued up commands.
34573484
* Invoked from the main loop when it's safe to execute received commands.
@@ -3968,11 +3995,15 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
39683995
/*
39693996
* Get the channel from the argument.
39703997
* Returns NULL if the handle is invalid.
3998+
* When "check_open" is TRUE check that the channel can be used.
3999+
* When "reading" is TRUE "check_open" considers typeahead useful.
4000+
* "part" is used to check typeahead, when -1 use the default part.
39714001
*/
39724002
channel_T *
3973-
get_channel_arg(typval_T *tv, int check_open)
4003+
get_channel_arg(typval_T *tv, int check_open, int reading, int part)
39744004
{
3975-
channel_T *channel = NULL;
4005+
channel_T *channel = NULL;
4006+
int has_readahead = FALSE;
39764007

39774008
if (tv->v_type == VAR_JOB)
39784009
{
@@ -3988,8 +4019,12 @@ get_channel_arg(typval_T *tv, int check_open)
39884019
EMSG2(_(e_invarg2), get_tv_string(tv));
39894020
return NULL;
39904021
}
4022+
if (channel != NULL && reading)
4023+
has_readahead = channel_has_readahead(channel,
4024+
part >= 0 ? part : channel_part_read(channel));
39914025

3992-
if (check_open && (channel == NULL || !channel_is_open(channel)))
4026+
if (check_open && (channel == NULL || (!channel_is_open(channel)
4027+
&& !(reading && has_readahead))))
39934028
{
39944029
EMSG(_("E906: not an open channel"));
39954030
return NULL;

src/eval.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10305,7 +10305,7 @@ f_ceil(typval_T *argvars, typval_T *rettv)
1030510305
static void
1030610306
f_ch_close(typval_T *argvars, typval_T *rettv UNUSED)
1030710307
{
10308-
channel_T *channel = get_channel_arg(&argvars[0], TRUE);
10308+
channel_T *channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0);
1030910309

1031010310
if (channel != NULL)
1031110311
{
@@ -10320,7 +10320,7 @@ f_ch_close(typval_T *argvars, typval_T *rettv UNUSED)
1032010320
static void
1032110321
f_ch_getbufnr(typval_T *argvars, typval_T *rettv)
1032210322
{
10323-
channel_T *channel = get_channel_arg(&argvars[0], TRUE);
10323+
channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
1032410324

1032510325
rettv->vval.v_number = -1;
1032610326
if (channel != NULL)
@@ -10347,7 +10347,7 @@ f_ch_getbufnr(typval_T *argvars, typval_T *rettv)
1034710347
static void
1034810348
f_ch_getjob(typval_T *argvars, typval_T *rettv)
1034910349
{
10350-
channel_T *channel = get_channel_arg(&argvars[0], TRUE);
10350+
channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
1035110351

1035210352
if (channel != NULL)
1035310353
{
@@ -10364,7 +10364,7 @@ f_ch_getjob(typval_T *argvars, typval_T *rettv)
1036410364
static void
1036510365
f_ch_info(typval_T *argvars, typval_T *rettv UNUSED)
1036610366
{
10367-
channel_T *channel = get_channel_arg(&argvars[0], TRUE);
10367+
channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
1036810368

1036910369
if (channel != NULL && rettv_dict_alloc(rettv) != FAIL)
1037010370
channel_info(channel, rettv->vval.v_dict);
@@ -10380,7 +10380,7 @@ f_ch_log(typval_T *argvars, typval_T *rettv UNUSED)
1038010380
channel_T *channel = NULL;
1038110381

1038210382
if (argvars[1].v_type != VAR_UNKNOWN)
10383-
channel = get_channel_arg(&argvars[1], TRUE);
10383+
channel = get_channel_arg(&argvars[1], FALSE, FALSE, 0);
1038410384

1038510385
ch_log(channel, (char *)msg);
1038610386
}
@@ -10476,7 +10476,7 @@ f_ch_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
1047610476
channel_T *channel;
1047710477
jobopt_T opt;
1047810478

10479-
channel = get_channel_arg(&argvars[0], TRUE);
10479+
channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
1048010480
if (channel == NULL)
1048110481
return;
1048210482
clear_job_options(&opt);
@@ -10498,7 +10498,7 @@ f_ch_status(typval_T *argvars, typval_T *rettv)
1049810498
rettv->v_type = VAR_STRING;
1049910499
rettv->vval.v_string = NULL;
1050010500

10501-
channel = get_channel_arg(&argvars[0], FALSE);
10501+
channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
1050210502
rettv->vval.v_string = vim_strsave((char_u *)channel_status(channel));
1050310503
}
1050410504
#endif

src/proto/channel.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ int channel_get_timeout(channel_T *channel, int part);
4848
void clear_job_options(jobopt_T *opt);
4949
void free_job_options(jobopt_T *opt);
5050
int get_job_options(typval_T *tv, jobopt_T *opt, int supported);
51-
channel_T *get_channel_arg(typval_T *tv, int check_open);
51+
channel_T *get_channel_arg(typval_T *tv, int check_open, int reading, int part);
5252
void job_unref(job_T *job);
5353
int free_unused_jobs_contents(int copyID, int mask);
5454
void free_unused_jobs(int copyID, int mask);

src/testdir/test_channel.vim

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,28 @@ func Test_out_close_cb()
10801080
endtry
10811081
endfunc
10821082

1083+
func Test_read_in_close_cb()
1084+
if !has('job')
1085+
return
1086+
endif
1087+
call ch_log('Test_read_in_close_cb()')
1088+
1089+
let s:received = ''
1090+
func! CloseHandler(chan)
1091+
let s:received = ch_read(a:chan)
1092+
endfunc
1093+
let job = job_start(s:python . " test_channel_pipe.py quit now",
1094+
\ {'close_cb': 'CloseHandler'})
1095+
call assert_equal("run", job_status(job))
1096+
try
1097+
call s:waitFor('s:received != ""')
1098+
call assert_equal('quit', s:received)
1099+
finally
1100+
call job_stop(job)
1101+
delfunc CloseHandler
1102+
endtry
1103+
endfunc
1104+
10831105
""""""""""
10841106

10851107
let s:unletResponse = ''

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,8 @@ static char *(features[]) =
753753

754754
static int included_patches[] =
755755
{ /* Add new patch number below this line */
756+
/**/
757+
1789,
756758
/**/
757759
1788,
758760
/**/

0 commit comments

Comments
 (0)