Patch 8.0.0107
Problem: When reading channel output in a timer, messages may go missing.
(Skywind)
Solution: Add the "drop" option. Write error messages in the channel log.
Don't have ch_canread() check for the channel being open.
Files: src/structs.h, src/channel.c, src/message.c, src/evalfunc.c,
src/proto/channel.pro, runtime/doc/channel.txt
*** ../vim-8.0.0106/src/structs.h 2016-11-24 15:09:03.405856662 +0100
--- src/structs.h 2016-12-01 13:04:30.094157140 +0100
***************
*** 1474,1479 ****
--- 1474,1480 ----
typval_T *jq_value;
jsonq_T *jq_next;
jsonq_T *jq_prev;
+ int jq_no_callback; /* TRUE when no callback was found */
};
struct cbq_S
***************
*** 1597,1602 ****
--- 1598,1604 ----
partial_T *ch_partial;
char_u *ch_close_cb; /* call when channel is closed */
partial_T *ch_close_partial;
+ int ch_drop_never;
job_T *ch_job; /* Job that uses this channel; this does not
* count as a reference to avoid a circular
***************
*** 1684,1689 ****
--- 1686,1692 ----
partial_T *jo_close_partial; /* not referenced! */
char_u *jo_exit_cb; /* not allocated! */
partial_T *jo_exit_partial; /* not referenced! */
+ int jo_drop_never;
int jo_waittime;
int jo_timeout;
int jo_out_timeout;
*** ../vim-8.0.0106/src/channel.c 2016-11-29 21:54:41.116260206 +0100
--- src/channel.c 2016-12-01 15:21:38.504292378 +0100
***************
*** 1195,1200 ****
--- 1195,1201 ----
if (opt->jo_set & JO_CLOSE_CALLBACK)
set_callback(&channel->ch_close_cb, &channel->ch_close_partial,
opt->jo_close_cb, opt->jo_close_partial);
+ channel->ch_drop_never = opt->jo_drop_never;
if ((opt->jo_set & JO_OUT_IO) && opt->jo_io[PART_OUT] == JIO_BUFFER)
{
***************
*** 1918,1923 ****
--- 1919,1925 ----
clear_tv(&listtv);
else
{
+ item->jq_no_callback = FALSE;
item->jq_value = alloc_tv();
if (item->jq_value == NULL)
{
***************
*** 2050,2060 ****
* When "id" is positive it must match the first number in the list.
* When "id" is zero or negative jut get the first message. But not the one
* with id ch_block_id.
* Return OK when found and return the value in "rettv".
* 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;
--- 2052,2068 ----
* When "id" is positive it must match the first number in the list.
* When "id" is zero or negative jut get the first message. But not the one
* with id ch_block_id.
+ * When "without_callback" is TRUE also get messages that were pushed back.
* Return OK when found and return the value in "rettv".
* Return FAIL otherwise.
*/
static int
! channel_get_json(
! channel_T *channel,
! ch_part_T part,
! int id,
! int without_callback,
! typval_T **rettv)
{
jsonq_T *head = &channel->ch_part[part].ch_json_head;
jsonq_T *item = head->jq_next;
***************
*** 2064,2073 ****
list_T *l = item->jq_value->vval.v_list;
typval_T *tv = &l->lv_first->li_tv;
! if ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
|| (id <= 0 && (tv->v_type != VAR_NUMBER
|| tv->vval.v_number == 0
! || tv->vval.v_number != channel->ch_part[part].ch_block_id)))
{
*rettv = item->jq_value;
if (tv->v_type == VAR_NUMBER)
--- 2072,2082 ----
list_T *l = item->jq_value->vval.v_list;
typval_T *tv = &l->lv_first->li_tv;
! if ((without_callback || !item->jq_no_callback)
! && ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
|| (id <= 0 && (tv->v_type != VAR_NUMBER
|| tv->vval.v_number == 0
! || tv->vval.v_number != channel->ch_part[part].ch_block_id))))
{
*rettv = item->jq_value;
if (tv->v_type == VAR_NUMBER)
***************
*** 2080,2085 ****
--- 2089,2153 ----
return FAIL;
}
+ /*
+ * Put back "rettv" into the JSON queue, there was no callback for it.
+ * Takes over the values in "rettv".
+ */
+ static void
+ channel_push_json(channel_T *channel, ch_part_T part, typval_T *rettv)
+ {
+ jsonq_T *head = &channel->ch_part[part].ch_json_head;
+ jsonq_T *item = head->jq_next;
+ jsonq_T *newitem;
+
+ if (head->jq_prev != NULL && head->jq_prev->jq_no_callback)
+ /* last item was pushed back, append to the end */
+ item = NULL;
+ else while (item != NULL && item->jq_no_callback)
+ /* append after the last item that was pushed back */
+ item = item->jq_next;
+
+ newitem = (jsonq_T *)alloc((unsigned)sizeof(jsonq_T));
+ if (newitem == NULL)
+ clear_tv(rettv);
+ else
+ {
+ newitem->jq_value = alloc_tv();
+ if (newitem->jq_value == NULL)
+ {
+ vim_free(newitem);
+ clear_tv(rettv);
+ }
+ else
+ {
+ newitem->jq_no_callback = FALSE;
+ *newitem->jq_value = *rettv;
+ if (item == NULL)
+ {
+ /* append to the end */
+ newitem->jq_prev = head->jq_prev;
+ head->jq_prev = newitem;
+ newitem->jq_next = NULL;
+ if (newitem->jq_prev == NULL)
+ head->jq_next = newitem;
+ else
+ newitem->jq_prev->jq_next = newitem;
+ }
+ else
+ {
+ /* append after "item" */
+ newitem->jq_prev = item;
+ newitem->jq_next = item->jq_next;
+ item->jq_next = newitem;
+ if (newitem->jq_next == NULL)
+ head->jq_prev = newitem;
+ else
+ newitem->jq_next->jq_prev = newitem;
+ }
+ }
+ }
+ }
+
#define CH_JSON_MAX_ARGS 4
/*
***************
*** 2410,2420 ****
int argc = 0;
/* Get any json message in the queue. */
! if (channel_get_json(channel, part, -1, &listtv) == FAIL)
{
/* Parse readahead, return when there is still no message. */
channel_parse_json(channel, part);
! if (channel_get_json(channel, part, -1, &listtv) == FAIL)
return FALSE;
}
--- 2478,2488 ----
int argc = 0;
/* Get any json message in the queue. */
! if (channel_get_json(channel, part, -1, FALSE, &listtv) == FAIL)
{
/* Parse readahead, return when there is still no message. */
channel_parse_json(channel, part);
! if (channel_get_json(channel, part, -1, FALSE, &listtv) == FAIL)
return FALSE;
}
***************
*** 2454,2460 ****
{
/* 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;
}
--- 2522,2528 ----
{
/* If there is a close callback it may use ch_read() to get the
* messages. */
! if (channel->ch_close_cb == NULL && !channel->ch_drop_never)
drop_messages(channel, part);
return FALSE;
}
***************
*** 2531,2537 ****
{
int done = FALSE;
! /* invoke the one-time callback with the matching nr */
for (cbitem = cbhead->cq_next; cbitem != NULL; cbitem = cbitem->cq_next)
if (cbitem->cq_seq_nr == seq_nr)
{
--- 2599,2605 ----
{
int done = FALSE;
! /* JSON or JS mode: invoke the one-time callback with the matching nr */
for (cbitem = cbhead->cq_next; cbitem != NULL; cbitem = cbitem->cq_next)
if (cbitem->cq_seq_nr == seq_nr)
{
***************
*** 2540,2546 ****
break;
}
if (!done)
! ch_logn(channel, "Dropping message %d without callback", seq_nr);
}
else if (callback != NULL || buffer != NULL)
{
--- 2608,2624 ----
break;
}
if (!done)
! {
! if (channel->ch_drop_never)
! {
! /* message must be read with ch_read() */
! channel_push_json(channel, part, listtv);
! listtv = NULL;
! }
! else
! ch_logn(channel, "Dropping message %d without callback",
! seq_nr);
! }
}
else if (callback != NULL || buffer != NULL)
{
***************
*** 2567,2573 ****
}
}
else
! ch_log(channel, "Dropping message");
if (listtv != NULL)
free_tv(listtv);
--- 2645,2651 ----
}
}
else
! ch_logn(channel, "Dropping message %d", seq_nr);
if (listtv != NULL)
free_tv(listtv);
***************
*** 2792,2800 ****
redraw_after_callback();
}
! /* any remaining messages are useless now */
! for (part = PART_SOCK; part < PART_IN; ++part)
! drop_messages(channel, part);
}
channel->ch_nb_close_cb = NULL;
--- 2870,2879 ----
redraw_after_callback();
}
! if (!channel->ch_drop_never)
! /* any remaining messages are useless now */
! for (part = PART_SOCK; part < PART_IN; ++part)
! drop_messages(channel, part);
}
channel->ch_nb_close_cb = NULL;
***************
*** 3091,3099 ****
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)();
}
/*
--- 3170,3178 ----
channel_close_now(channel_T *channel)
{
ch_log(channel, "Closing channel because all readable fds are closed");
if (channel->ch_nb_close_cb != NULL)
(*channel->ch_nb_close_cb)();
+ channel_close(channel, TRUE);
}
/*
***************
*** 3243,3249 ****
* When "id" is -1 accept any message;
* Blocks until the message is received or the timeout is reached.
*/
! int
channel_read_json_block(
channel_T *channel,
ch_part_T part,
--- 3322,3328 ----
* When "id" is -1 accept any message;
* Blocks until the message is received or the timeout is reached.
*/
! static int
channel_read_json_block(
channel_T *channel,
ch_part_T part,
***************
*** 3264,3270 ****
more = channel_parse_json(channel, part);
/* search for message "id" */
! if (channel_get_json(channel, part, id, rettv) == OK)
{
chanpart->ch_block_id = 0;
return OK;
--- 3343,3349 ----
more = channel_parse_json(channel, part);
/* search for message "id" */
! if (channel_get_json(channel, part, id, TRUE, rettv) == OK)
{
chanpart->ch_block_id = 0;
return OK;
***************
*** 4290,4295 ****
--- 4369,4388 ----
return FAIL;
}
}
+ else if (STRCMP(hi->hi_key, "drop") == 0)
+ {
+ int never = FALSE;
+ val = get_tv_string(item);
+
+ if (STRCMP(val, "never") == 0)
+ never = TRUE;
+ else if (STRCMP(val, "auto") != 0)
+ {
+ EMSG2(_(e_invarg2), "drop");
+ return FAIL;
+ }
+ opt->jo_drop_never = never;
+ }
else if (STRCMP(hi->hi_key, "exit_cb") == 0)
{
if (!(supported & JO_EXIT_CB))
*** ../vim-8.0.0106/src/message.c 2016-11-10 20:01:41.193582919 +0100
--- src/message.c 2016-12-01 13:50:25.124167776 +0100
***************
*** 42,47 ****
--- 42,50 ----
static char_u *confirm_msg = NULL; /* ":confirm" message */
static char_u *confirm_msg_tail; /* tail of confirm_msg */
#endif
+ #ifdef FEAT_JOB_CHANNEL
+ static int emsg_to_channel_log = FALSE;
+ #endif
struct msg_hist
{
***************
*** 166,171 ****
--- 169,182 ----
&& STRCMP(s, last_msg_hist->msg)))
add_msg_hist(s, -1, attr);
+ #ifdef FEAT_JOB_CHANNEL
+ if (emsg_to_channel_log)
+ {
+ /* Write message in the channel log. */
+ ch_logs(NULL, "ERROR: %s", (char *)s);
+ }
+ #endif
+
/* When displaying keep_msg, don't let msg_start() free it, caller must do
* that. */
if (s == keep_msg)
***************
*** 556,561 ****
--- 567,573 ----
{
int attr;
char_u *p;
+ int r;
#ifdef FEAT_EVAL
int ignore = FALSE;
int severe;
***************
*** 624,629 ****
--- 636,644 ----
}
redir_write(s, -1);
}
+ #ifdef FEAT_JOB_CHANNEL
+ ch_logs(NULL, "ERROR: %s", (char *)s);
+ #endif
return TRUE;
}
***************
*** 650,655 ****
--- 665,673 ----
* and a redraw is expected because
* msg_scrolled is non-zero */
+ #ifdef FEAT_JOB_CHANNEL
+ emsg_to_channel_log = TRUE;
+ #endif
/*
* Display name and line number for the source of the error.
*/
***************
*** 659,665 ****
* Display the error message itself.
*/
msg_nowait = FALSE; /* wait for this msg */
! return msg_attr(s, attr);
}
--- 677,688 ----
* Display the error message itself.
*/
msg_nowait = FALSE; /* wait for this msg */
! r = msg_attr(s, attr);
!
! #ifdef FEAT_JOB_CHANNEL
! emsg_to_channel_log = FALSE;
! #endif
! return r;
}
*** ../vim-8.0.0106/src/evalfunc.c 2016-11-29 21:54:41.116260206 +0100
--- src/evalfunc.c 2016-12-01 15:17:06.322073618 +0100
***************
*** 1786,1792 ****
static void
f_ch_canread(typval_T *argvars, typval_T *rettv)
{
! channel_T *channel = get_channel_arg(&argvars[0], TRUE, TRUE, 0);
rettv->vval.v_number = 0;
if (channel != NULL)
--- 1786,1792 ----
static void
f_ch_canread(typval_T *argvars, typval_T *rettv)
{
! channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0);
rettv->vval.v_number = 0;
if (channel != NULL)
*** ../vim-8.0.0106/src/proto/channel.pro 2016-11-29 21:54:41.116260206
+0100
--- src/proto/channel.pro 2016-12-01 12:47:07.212955490 +0100
***************
*** 33,39 ****
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);
--- 33,38 ----
*** ../vim-8.0.0106/runtime/doc/channel.txt 2016-11-29 21:54:41.120260177
+0100
--- runtime/doc/channel.txt 2016-12-01 12:20:43.087290042 +0100
***************
*** 155,161 ****
func MyCloseHandler(channel)
< Vim will invoke callbacks that handle data before invoking
close_cb, thus when this function is called no more data will
! be received.
*waittime*
"waittime" The time to wait for the connection to be made in
milliseconds. A negative number waits forever.
--- 155,167 ----
func MyCloseHandler(channel)
< Vim will invoke callbacks that handle data before invoking
close_cb, thus when this function is called no more data will
! be passed to the callbacks.
! *channel-drop*
! "drop" Specifies when to drop messages:
! "auto" When there is no callback to handle a message.
! The "close_cb" is also considered for this.
! "never" All messages will be kept.
!
*waittime*
"waittime" The time to wait for the connection to be made in
milliseconds. A negative number waits forever.
***************
*** 600,610 ****
"close_cb": handler Callback for when the channel is closed. Same as
"close_cb" on |ch_open()|, see |close_cb|.
*job-exit_cb*
"exit_cb": handler Callback for when the job ends. The arguments are the
job and the exit status.
! Vim checks about every 10 seconds for jobs that ended.
! The check also be triggered by calling |job_status()|,
! which may then invoke the exit_cb handler.
Note that data can be buffered, callbacks may still be
called after the process ends.
*job-timeout*
--- 606,621 ----
"close_cb": handler Callback for when the channel is closed. Same as
"close_cb" on |ch_open()|, see |close_cb|.
*job-exit_cb*
+ "drop" Specifies when to drop messages. Same as
"drop" on
+ |ch_open()|, see |channel-drop|. For "auto" the
+ exit_cb is not considered.
+
"exit_cb": handler Callback for when the job ends. The arguments are the
job and the exit status.
! Vim checks up to 10 times per second for jobs that
! ended. The check can also be triggered by calling
! |job_status()|, which may then invoke the exit_cb
! handler.
Note that data can be buffered, callbacks may still be
called after the process ends.
*job-timeout*
*** ../vim-8.0.0106/src/version.c 2016-11-29 22:10:44.221151470 +0100
--- src/version.c 2016-12-01 12:21:52.086839902 +0100
***************
*** 766,767 ****
--- 766,769 ----
{ /* Add new patch number below this line */
+ /**/
+ 107,
/**/
--
hundred-and-one symptoms of being an internet addict:
62. If your doorbell rings, you think that new mail has arrived. And then
you're disappointed that it's only someone at the door.
/// 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.