mirror of
https://github.com/vim/vim.git
synced 2025-09-26 04:04:07 -04:00
patch 8.1.1512: ch_evalexpr() hangs when used recursively
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.
This commit is contained in:
@@ -2153,11 +2153,67 @@ remove_json_node(jsonq_T *head, jsonq_T *node)
|
|||||||
vim_free(node);
|
vim_free(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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".
|
* 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 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
|
* When "id" is zero or negative jut get the first message. But not one
|
||||||
* with id ch_block_id.
|
* in the ch_block_ids list.
|
||||||
* When "without_callback" is TRUE also get messages that were pushed back.
|
* When "without_callback" is TRUE also get messages that were pushed back.
|
||||||
* Return OK when found and return the value in "rettv".
|
* Return OK when found and return the value in "rettv".
|
||||||
* Return FAIL otherwise.
|
* Return FAIL otherwise.
|
||||||
@@ -2182,7 +2238,8 @@ channel_get_json(
|
|||||||
&& ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
|
&& ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
|
||||||
|| (id <= 0 && (tv->v_type != VAR_NUMBER
|
|| (id <= 0 && (tv->v_type != VAR_NUMBER
|
||||||
|| tv->vval.v_number == 0
|
|| tv->vval.v_number == 0
|
||||||
|| tv->vval.v_number != channel->ch_part[part].ch_block_id))))
|
|| !channel_has_block_id(
|
||||||
|
&channel->ch_part[part], tv->vval.v_number)))))
|
||||||
{
|
{
|
||||||
*rettv = item->jq_value;
|
*rettv = item->jq_value;
|
||||||
if (tv->v_type == VAR_NUMBER)
|
if (tv->v_type == VAR_NUMBER)
|
||||||
@@ -3050,6 +3107,7 @@ channel_clear_one(channel_T *channel, ch_part_T part)
|
|||||||
}
|
}
|
||||||
|
|
||||||
free_callback(&ch_part->ch_callback);
|
free_callback(&ch_part->ch_callback);
|
||||||
|
ga_clear(&ch_part->ch_block_ids);
|
||||||
|
|
||||||
while (ch_part->ch_writeque.wq_next != NULL)
|
while (ch_part->ch_writeque.wq_next != NULL)
|
||||||
remove_from_writeque(&ch_part->ch_writeque,
|
remove_from_writeque(&ch_part->ch_writeque,
|
||||||
@@ -3480,6 +3538,8 @@ channel_read_block(
|
|||||||
* result in "rettv".
|
* result in "rettv".
|
||||||
* When "id" is -1 accept any message;
|
* 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.
|
||||||
|
* In corner cases this can be called recursively, that is why ch_block_ids is
|
||||||
|
* a list.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
channel_read_json_block(
|
channel_read_json_block(
|
||||||
@@ -3494,17 +3554,19 @@ channel_read_json_block(
|
|||||||
int timeout;
|
int timeout;
|
||||||
chanpart_T *chanpart = &channel->ch_part[part];
|
chanpart_T *chanpart = &channel->ch_part[part];
|
||||||
|
|
||||||
ch_log(channel, "Reading JSON");
|
ch_log(channel, "Blocking read JSON for id %d", id);
|
||||||
if (id != -1)
|
if (id >= 0)
|
||||||
chanpart->ch_block_id = id;
|
channel_add_block_id(chanpart, id);
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
more = channel_parse_json(channel, part);
|
more = channel_parse_json(channel, part);
|
||||||
|
|
||||||
/* search for message "id" */
|
// search for message "id"
|
||||||
if (channel_get_json(channel, part, id, TRUE, rettv) == OK)
|
if (channel_get_json(channel, part, id, TRUE, rettv) == OK)
|
||||||
{
|
{
|
||||||
chanpart->ch_block_id = 0;
|
if (id >= 0)
|
||||||
|
channel_remove_block_id(chanpart, id);
|
||||||
|
ch_log(channel, "Received JSON for id %d", id);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3551,7 +3613,7 @@ channel_read_json_block(
|
|||||||
if (timeout == timeout_arg)
|
if (timeout == timeout_arg)
|
||||||
{
|
{
|
||||||
if (fd != INVALID_FD)
|
if (fd != INVALID_FD)
|
||||||
ch_log(channel, "Timed out");
|
ch_log(channel, "Timed out on id %d", id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3559,7 +3621,8 @@ channel_read_json_block(
|
|||||||
channel_read(channel, part, "channel_read_json_block");
|
channel_read(channel, part, "channel_read_json_block");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chanpart->ch_block_id = 0;
|
if (id >= 0)
|
||||||
|
channel_remove_block_id(chanpart, id);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1680,8 +1680,8 @@ typedef struct {
|
|||||||
|
|
||||||
readq_T ch_head; /* header for circular raw read queue */
|
readq_T ch_head; /* header for circular raw read queue */
|
||||||
jsonq_T ch_json_head; /* header for circular json read queue */
|
jsonq_T ch_json_head; /* header for circular json read queue */
|
||||||
int ch_block_id; /* ID that channel_read_json_block() is
|
garray_T ch_block_ids; /* list of IDs that channel_read_json_block()
|
||||||
waiting for */
|
is waiting for */
|
||||||
/* When ch_wait_len is non-zero use ch_deadline to wait for incomplete
|
/* 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 to be complete. The value is the length of the incomplete
|
||||||
* message when the deadline was set. If it gets longer (something was
|
* message when the deadline was set. If it gets longer (something was
|
||||||
|
@@ -777,6 +777,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 */
|
||||||
|
/**/
|
||||||
|
1512,
|
||||||
/**/
|
/**/
|
||||||
1511,
|
1511,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user