Patch 8.1.1512
Problem: ch_evalexpr() hangs when used recursively. (Paul Jolly)
Solution: Change ch_block_id from a single number to a list of IDs to wait
on.
Files: src/channel.c, src/structs.h
*** ../vim-8.1.1511/src/channel.c 2019-06-03 21:14:55.129048884 +0200
--- src/channel.c 2019-06-09 19:42:30.139390195 +0200
***************
*** 2154,2163 ****
}
/*
* Get a message from the JSON queue for channel "channel".
* 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.
--- 2154,2219 ----
}
/*
+ * Add "id" to the list of JSON message IDs we are waiting on.
+ */
+ static void
+ channel_add_block_id(chanpart_T *chanpart, int id)
+ {
+ garray_T *gap = &chanpart->ch_block_ids;
+
+ if (gap->ga_growsize == 0)
+ ga_init2(gap, (int)sizeof(int), 10);
+ if (ga_grow(gap, 1) == OK)
+ {
+ ((int *)gap->ga_data)[gap->ga_len] = id;
+ ++gap->ga_len;
+ }
+ }
+
+ /*
+ * Remove "id" from the list of JSON message IDs we are waiting on.
+ */
+ static void
+ channel_remove_block_id(chanpart_T *chanpart, int id)
+ {
+ garray_T *gap = &chanpart->ch_block_ids;
+ int i;
+
+ for (i = 0; i < gap->ga_len; ++i)
+ if (((int *)gap->ga_data)[i] == id)
+ {
+ --gap->ga_len;
+ if (i < gap->ga_len)
+ {
+ int *p = ((int *)gap->ga_data) + i;
+
+ mch_memmove(p, p + 1, (gap->ga_len - i) * sizeof(int));
+ }
+ return;
+ }
+ siemsg("INTERNAL: channel_remove_block_id: cannot find id %d", id);
+ }
+
+ /*
+ * Return TRUE if "id" is in the list of JSON message IDs we are waiting on.
+ */
+ static int
+ channel_has_block_id(chanpart_T *chanpart, int id)
+ {
+ garray_T *gap = &chanpart->ch_block_ids;
+ int i;
+
+ for (i = 0; i < gap->ga_len; ++i)
+ if (((int *)gap->ga_data)[i] == id)
+ return TRUE;
+ return FALSE;
+ }
+
+ /*
* Get a message from the JSON queue for channel "channel".
* 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 one
! * in the ch_block_ids list.
* 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.
***************
*** 2182,2188 ****
&& ((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)
--- 2238,2245 ----
&& ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
|| (id <= 0 && (tv->v_type != VAR_NUMBER
|| tv->vval.v_number == 0
! || !channel_has_block_id(
! &channel->ch_part[part], tv->vval.v_number)))))
{
*rettv = item->jq_value;
if (tv->v_type == VAR_NUMBER)
***************
*** 3050,3055 ****
--- 3107,3113 ----
}
free_callback(&ch_part->ch_callback);
+ ga_clear(&ch_part->ch_block_ids);
while (ch_part->ch_writeque.wq_next != NULL)
remove_from_writeque(&ch_part->ch_writeque,
***************
*** 3480,3485 ****
--- 3538,3545 ----
* result in "rettv".
* When "id" is -1 accept any message;
* Blocks until the message is received or the timeout is reached.
+ * In corner cases this can be called recursively, that is why ch_block_ids is
+ * a list.
*/
static int
channel_read_json_block(
***************
*** 3494,3510 ****
int timeout;
chanpart_T *chanpart = &channel->ch_part[part];
! ch_log(channel, "Reading JSON");
! if (id != -1)
! chanpart->ch_block_id = id;
for (;;)
{
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;
}
--- 3554,3572 ----
int timeout;
chanpart_T *chanpart = &channel->ch_part[part];
! ch_log(channel, "Blocking read JSON for id %d", id);
! if (id >= 0)
! channel_add_block_id(chanpart, id);
for (;;)
{
more = channel_parse_json(channel, part);
! // search for message "id"
if (channel_get_json(channel, part, id, TRUE, rettv) == OK)
{
! if (id >= 0)
! channel_remove_block_id(chanpart, id);
! ch_log(channel, "Received JSON for id %d", id);
return OK;
}
***************
*** 3551,3557 ****
if (timeout == timeout_arg)
{
if (fd != INVALID_FD)
! ch_log(channel, "Timed out");
break;
}
}
--- 3613,3619 ----
if (timeout == timeout_arg)
{
if (fd != INVALID_FD)
! ch_log(channel, "Timed out on id %d", id);
break;
}
}
***************
*** 3559,3565 ****
channel_read(channel, part, "channel_read_json_block");
}
}
! chanpart->ch_block_id = 0;
return FAIL;
}
--- 3621,3628 ----
channel_read(channel, part, "channel_read_json_block");
}
}
! if (id >= 0)
! channel_remove_block_id(chanpart, id);
return FAIL;
}
*** ../vim-8.1.1511/src/structs.h 2019-06-08 16:06:25.454274085 +0200
--- src/structs.h 2019-06-09 19:13:44.227389731 +0200
***************
*** 1680,1687 ****
readq_T ch_head; /* header for circular raw read queue */
jsonq_T ch_json_head; /* header for circular json read queue */
! int ch_block_id; /* ID that channel_read_json_block() is
! waiting for */
/* When ch_wait_len is non-zero use ch_deadline to wait for incomplete
* message to be complete. The value is the length of the incomplete
* message when the deadline was set. If it gets longer (something was
--- 1680,1687 ----
readq_T ch_head; /* header for circular raw read queue */
jsonq_T ch_json_head; /* header for circular json read queue */
! garray_T ch_block_ids; /* list of IDs that channel_read_json_block()
! is waiting for */
/* When ch_wait_len is non-zero use ch_deadline to wait for incomplete
* message to be complete. The value is the length of the incomplete
* message when the deadline was set. If it gets longer (something was
*** ../vim-8.1.1511/src/version.c 2019-06-09 18:04:09.839426999 +0200
--- src/version.c 2019-06-09 19:51:03.112811393 +0200
***************
*** 779,780 ****
--- 779,782 ----
{ /* Add new patch number below this line */
+ /**/
+ 1512,
/**/
--
Amazing but true: If all the salmon caught in Canada in one year were laid
end to end across the Sahara Desert, the smell would be absolutely awful.
/// Bram Moolenaar -- Br...@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 ///