1
0
forked from aniani/vim

patch 7.4.1372

Problem:    channel read implementation is incomplete.
Solution:   Add ch_read() and options for ch_readraw().
This commit is contained in:
Bram Moolenaar
2016-02-20 19:56:13 +01:00
parent fffd5560c6
commit 6f3a544228
6 changed files with 141 additions and 27 deletions

View File

@@ -1696,12 +1696,11 @@ channel_read(channel_T *channel, int part, char *func)
* Returns NULL in case of error or timeout. * Returns NULL in case of error or timeout.
*/ */
char_u * char_u *
channel_read_block(channel_T *channel, int part) channel_read_block(channel_T *channel, int part, int timeout)
{ {
char_u *buf; char_u *buf;
char_u *msg; char_u *msg;
ch_mode_T mode = channel->ch_part[part].ch_mode; ch_mode_T mode = channel->ch_part[part].ch_mode;
int timeout = channel->ch_part[part].ch_timeout;
sock_T fd = channel->ch_part[part].ch_fd; sock_T fd = channel->ch_part[part].ch_fd;
char_u *nl; char_u *nl;
@@ -1753,15 +1752,22 @@ channel_read_block(channel_T *channel, int part)
/* /*
* Read one JSON message with ID "id" from "channel"/"part" and store the * Read one JSON message with ID "id" from "channel"/"part" and store the
* result in "rettv". * result in "rettv".
* When "id" is -1 accept any message;
* Blocks until the message is received or the timeout is reached. * Blocks until the message is received or the timeout is reached.
*/ */
int int
channel_read_json_block(channel_T *channel, int part, int id, typval_T **rettv) channel_read_json_block(
channel_T *channel,
int part,
int timeout,
int id,
typval_T **rettv)
{ {
int more; int more;
sock_T fd; sock_T fd;
ch_log(channel, "Reading JSON"); ch_log(channel, "Reading JSON");
if (id != -1)
channel->ch_part[part].ch_block_id = id; channel->ch_part[part].ch_block_id = id;
for (;;) for (;;)
{ {
@@ -1781,10 +1787,9 @@ channel_read_json_block(channel_T *channel, int part, int id, typval_T **rettv)
if (channel_parse_messages()) if (channel_parse_messages())
continue; continue;
/* Wait for up to the channel timeout. */ /* Wait for up to the timeout. */
fd = channel->ch_part[part].ch_fd; fd = channel->ch_part[part].ch_fd;
if (fd == INVALID_FD || channel_wait(channel, fd, if (fd == INVALID_FD || channel_wait(channel, fd, timeout) == FAIL)
channel->ch_part[part].ch_timeout) == FAIL)
break; break;
channel_read(channel, part, "channel_read_json_block"); channel_read(channel, part, "channel_read_json_block");
} }
@@ -2161,4 +2166,13 @@ channel_get_mode(channel_T *channel, int part)
return channel->ch_part[part].ch_mode; return channel->ch_part[part].ch_mode;
} }
/*
* Return the timeout of "channel"/"part"
*/
int
channel_get_timeout(channel_T *channel, int part)
{
return channel->ch_part[part].ch_timeout;
}
#endif /* FEAT_CHANNEL */ #endif /* FEAT_CHANNEL */

View File

@@ -507,6 +507,7 @@ static void f_ch_close(typval_T *argvars, typval_T *rettv);
static void f_ch_log(typval_T *argvars, typval_T *rettv); static void f_ch_log(typval_T *argvars, typval_T *rettv);
static void f_ch_logfile(typval_T *argvars, typval_T *rettv); static void f_ch_logfile(typval_T *argvars, typval_T *rettv);
static void f_ch_open(typval_T *argvars, typval_T *rettv); static void f_ch_open(typval_T *argvars, typval_T *rettv);
static void f_ch_read(typval_T *argvars, typval_T *rettv);
static void f_ch_readraw(typval_T *argvars, typval_T *rettv); static void f_ch_readraw(typval_T *argvars, typval_T *rettv);
static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv); static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
static void f_ch_sendraw(typval_T *argvars, typval_T *rettv); static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
@@ -8129,6 +8130,7 @@ static struct fst
{"ch_log", 1, 2, f_ch_log}, {"ch_log", 1, 2, f_ch_log},
{"ch_logfile", 1, 2, f_ch_logfile}, {"ch_logfile", 1, 2, f_ch_logfile},
{"ch_open", 1, 2, f_ch_open}, {"ch_open", 1, 2, f_ch_open},
{"ch_read", 1, 2, f_ch_read},
{"ch_readraw", 1, 2, f_ch_readraw}, {"ch_readraw", 1, 2, f_ch_readraw},
{"ch_sendexpr", 2, 3, f_ch_sendexpr}, {"ch_sendexpr", 2, 3, f_ch_sendexpr},
{"ch_sendraw", 2, 3, f_ch_sendraw}, {"ch_sendraw", 2, 3, f_ch_sendraw},
@@ -9881,7 +9883,7 @@ get_callback(typval_T *arg)
get_job_options(typval_T *tv, jobopt_T *opt, int supported) get_job_options(typval_T *tv, jobopt_T *opt, int supported)
{ {
typval_T *item; typval_T *item;
char_u *mode; char_u *val;
dict_T *dict; dict_T *dict;
int todo; int todo;
hashitem_T *hi; hashitem_T *hi;
@@ -9909,18 +9911,18 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
if (!(supported & JO_MODE)) if (!(supported & JO_MODE))
break; break;
opt->jo_set |= JO_MODE; opt->jo_set |= JO_MODE;
mode = get_tv_string(item); val = get_tv_string(item);
if (STRCMP(mode, "nl") == 0) if (STRCMP(val, "nl") == 0)
opt->jo_mode = MODE_NL; opt->jo_mode = MODE_NL;
else if (STRCMP(mode, "raw") == 0) else if (STRCMP(val, "raw") == 0)
opt->jo_mode = MODE_RAW; opt->jo_mode = MODE_RAW;
else if (STRCMP(mode, "js") == 0) else if (STRCMP(val, "js") == 0)
opt->jo_mode = MODE_JS; opt->jo_mode = MODE_JS;
else if (STRCMP(mode, "json") == 0) else if (STRCMP(val, "json") == 0)
opt->jo_mode = MODE_JSON; opt->jo_mode = MODE_JSON;
else else
{ {
EMSG2(_(e_invarg2), mode); EMSG2(_(e_invarg2), val);
return FAIL; return FAIL;
} }
} }
@@ -9950,6 +9952,27 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
opt->jo_set |= JO_TIMEOUT; opt->jo_set |= JO_TIMEOUT;
opt->jo_timeout = get_tv_number(item); opt->jo_timeout = get_tv_number(item);
} }
else if (STRCMP(hi->hi_key, "part") == 0)
{
if (!(supported & JO_PART))
break;
opt->jo_set |= JO_PART;
val = get_tv_string(item);
if (STRCMP(val, "err") == 0)
opt->jo_part = PART_ERR;
else
{
EMSG2(_(e_invarg2), val);
return FAIL;
}
}
else if (STRCMP(hi->hi_key, "id") == 0)
{
if (!(supported & JO_ID))
break;
opt->jo_set |= JO_ID;
opt->jo_id = get_tv_number(item);
}
else else
break; break;
--todo; --todo;
@@ -10107,27 +10130,74 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
} }
/* /*
* "ch_readraw()" function * Common for ch_read() and ch_readraw().
*/ */
static void static void
f_ch_readraw(typval_T *argvars, typval_T *rettv) common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
{ {
channel_T *channel; channel_T *channel;
int part; int part;
jobopt_T opt;
int mode;
int timeout;
int id = -1;
typval_T *listtv = NULL;
/* return an empty string by default */ /* return an empty string by default */
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL; rettv->vval.v_string = NULL;
/* TODO: use timeout from the options */ opt.jo_set = 0;
/* TODO: read from stderr */ if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID)
== FAIL)
return;
channel = get_channel_arg(&argvars[0]); channel = get_channel_arg(&argvars[0]);
if (channel != NULL) if (channel != NULL)
{ {
if (opt.jo_set & JO_PART)
part = opt.jo_part;
else
part = channel_part_read(channel); part = channel_part_read(channel);
rettv->vval.v_string = channel_read_block(channel, part); mode = channel_get_mode(channel, part);
timeout = channel_get_timeout(channel, part);
if (opt.jo_set & JO_TIMEOUT)
timeout = opt.jo_timeout;
if (raw || mode == MODE_RAW || mode == MODE_NL)
rettv->vval.v_string = channel_read_block(channel, part, timeout);
else
{
if (opt.jo_set & JO_ID)
id = opt.jo_id;
channel_read_json_block(channel, part, timeout, id, &listtv);
if (listtv != NULL)
*rettv = *listtv;
else
{
rettv->v_type = VAR_SPECIAL;
rettv->vval.v_number = VVAL_NONE;
} }
}
}
}
/*
* "ch_read()" function
*/
static void
f_ch_read(typval_T *argvars, typval_T *rettv)
{
common_channel_read(argvars, rettv, FALSE);
}
/*
* "ch_readraw()" function
*/
static void
f_ch_readraw(typval_T *argvars, typval_T *rettv)
{
common_channel_read(argvars, rettv, TRUE);
} }
/* /*
@@ -10177,6 +10247,7 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
ch_mode_T ch_mode; ch_mode_T ch_mode;
int part_send; int part_send;
int part_read; int part_read;
int timeout;
/* return an empty string by default */ /* return an empty string by default */
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
@@ -10204,7 +10275,10 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
vim_free(text); vim_free(text);
if (channel != NULL) if (channel != NULL)
{ {
if (channel_read_json_block(channel, part_read, id, &listtv) == OK) /* TODO: timeout from options */
timeout = channel_get_timeout(channel, part_read);
if (channel_read_json_block(channel, part_read, timeout, id, &listtv)
== OK)
{ {
list_T *list = listtv->vval.v_list; list_T *list = listtv->vval.v_list;
@@ -10227,6 +10301,7 @@ f_ch_sendraw(typval_T *argvars, typval_T *rettv)
char_u *text; char_u *text;
channel_T *channel; channel_T *channel;
int part_read; int part_read;
int timeout;
/* return an empty string by default */ /* return an empty string by default */
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
@@ -10235,7 +10310,11 @@ f_ch_sendraw(typval_T *argvars, typval_T *rettv)
text = get_tv_string_buf(&argvars[1], buf); text = get_tv_string_buf(&argvars[1], buf);
channel = send_common(argvars, text, 0, "sendraw", &part_read); channel = send_common(argvars, text, 0, "sendraw", &part_read);
if (channel != NULL) if (channel != NULL)
rettv->vval.v_string = channel_read_block(channel, part_read); {
/* TODO: timeout from options */
timeout = channel_get_timeout(channel, part_read);
rettv->vval.v_string = channel_read_block(channel, part_read, timeout);
}
} }
/* /*

View File

@@ -23,9 +23,9 @@ void channel_clear(channel_T *channel);
void channel_free_all(void); void channel_free_all(void);
int channel_get_id(void); int channel_get_id(void);
void channel_read(channel_T *channel, int part, char *func); void channel_read(channel_T *channel, int part, char *func);
char_u *channel_read_block(channel_T *channel, int part); char_u *channel_read_block(channel_T *channel, int part, int timeout);
int channel_read_json_block(channel_T *channel, int part, int id, typval_T **rettv); int channel_read_json_block(channel_T *channel, int part, int timeout, int id, typval_T **rettv);
channel_T *channel_fd2channel(sock_T fd, int *part); channel_T *channel_fd2channel(sock_T fd, int *partp);
void channel_handle_events(void); void channel_handle_events(void);
int channel_send(channel_T *channel, int part, char_u *buf, char *fun); int channel_send(channel_T *channel, int part, char_u *buf, char *fun);
int channel_poll_setup(int nfd_in, void *fds_in); int channel_poll_setup(int nfd_in, void *fds_in);
@@ -37,4 +37,5 @@ int set_ref_in_channel(int copyID);
int channel_part_send(channel_T *channel); int channel_part_send(channel_T *channel);
int channel_part_read(channel_T *channel); int channel_part_read(channel_T *channel);
ch_mode_T channel_get_mode(channel_T *channel, int part); ch_mode_T channel_get_mode(channel_T *channel, int part);
int channel_get_timeout(channel_T *channel, int part);
/* vim: set ft=c : */ /* vim: set ft=c : */

View File

@@ -1377,6 +1377,8 @@ struct channel_S {
#define JO_CALLBACK 2 /* channel callback */ #define JO_CALLBACK 2 /* channel callback */
#define JO_WAITTIME 4 /* only for ch_open() */ #define JO_WAITTIME 4 /* only for ch_open() */
#define JO_TIMEOUT 8 /* all timeouts */ #define JO_TIMEOUT 8 /* all timeouts */
#define JO_PART 16 /* "part" */
#define JO_ID 32 /* "id" */
#define JO_ALL 0xffffff #define JO_ALL 0xffffff
/* /*
@@ -1390,6 +1392,8 @@ typedef struct
char_u *jo_callback; /* not allocated! */ char_u *jo_callback; /* not allocated! */
int jo_waittime; int jo_waittime;
int jo_timeout; int jo_timeout;
int jo_part;
int jo_id;
} jobopt_T; } jobopt_T;

View File

@@ -184,6 +184,21 @@ func s:communicate(port)
call assert_equal('ok', ch_sendexpr(handle, 'empty-request')) call assert_equal('ok', ch_sendexpr(handle, 'empty-request'))
" Reading while there is nothing available.
call assert_equal(v:none, ch_read(handle, {'timeout': 0}))
let start = reltime()
call assert_equal(v:none, ch_read(handle, {'timeout': 333}))
let elapsed = reltime(start)
call assert_true(reltimefloat(elapsed) > 0.3)
call assert_true(reltimefloat(elapsed) < 0.6)
" Send without waiting for a response, then wait for a response.
call ch_sendexpr(handle, 'wait a bit', {'callback': 0})
let resp = ch_read(handle)
call assert_equal(type([]), type(resp))
call assert_equal(type(11), type(resp[0]))
call assert_equal('waited', resp[1])
" make the server quit, can't check if this works, should not hang. " make the server quit, can't check if this works, should not hang.
call ch_sendexpr(handle, '!quit!', {'callback': 0}) call ch_sendexpr(handle, '!quit!', {'callback': 0})
endfunc endfunc
@@ -292,8 +307,7 @@ func Test_connect_waittime()
" Oops, port does exists. " Oops, port does exists.
call ch_close(handle) call ch_close(handle)
else else
" Failed connection doesn't wait the full time on Unix. " Failed connection should wait about 500 msec.
" TODO: why is MS-Windows different?
let elapsed = reltime(start) let elapsed = reltime(start)
call assert_true(reltimefloat(elapsed) < 1.0) call assert_true(reltimefloat(elapsed) < 1.0)
endif endif

View File

@@ -747,6 +747,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1372,
/**/ /**/
1371, 1371,
/**/ /**/