comparison src/channel.c @ 17024:727f8cc87a45 v8.1.1512

patch 8.1.1512: ch_evalexpr() hangs when used recursively commit https://github.com/vim/vim/commit/38ea784fecf7921dca83ddc75fe9cb40708521b2 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jun 9 19:51:58 2019 +0200 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.
author Bram Moolenaar <Bram@vim.org>
date Sun, 09 Jun 2019 20:00:06 +0200
parents a5e3509b33ca
children ebe9aab81898
comparison
equal deleted inserted replaced
17023:fd131767cde7 17024:727f8cc87a45
2152 node->jq_next->jq_prev = node->jq_prev; 2152 node->jq_next->jq_prev = node->jq_prev;
2153 vim_free(node); 2153 vim_free(node);
2154 } 2154 }
2155 2155
2156 /* 2156 /*
2157 * Add "id" to the list of JSON message IDs we are waiting on.
2158 */
2159 static void
2160 channel_add_block_id(chanpart_T *chanpart, int id)
2161 {
2162 garray_T *gap = &chanpart->ch_block_ids;
2163
2164 if (gap->ga_growsize == 0)
2165 ga_init2(gap, (int)sizeof(int), 10);
2166 if (ga_grow(gap, 1) == OK)
2167 {
2168 ((int *)gap->ga_data)[gap->ga_len] = id;
2169 ++gap->ga_len;
2170 }
2171 }
2172
2173 /*
2174 * Remove "id" from the list of JSON message IDs we are waiting on.
2175 */
2176 static void
2177 channel_remove_block_id(chanpart_T *chanpart, int id)
2178 {
2179 garray_T *gap = &chanpart->ch_block_ids;
2180 int i;
2181
2182 for (i = 0; i < gap->ga_len; ++i)
2183 if (((int *)gap->ga_data)[i] == id)
2184 {
2185 --gap->ga_len;
2186 if (i < gap->ga_len)
2187 {
2188 int *p = ((int *)gap->ga_data) + i;
2189
2190 mch_memmove(p, p + 1, (gap->ga_len - i) * sizeof(int));
2191 }
2192 return;
2193 }
2194 siemsg("INTERNAL: channel_remove_block_id: cannot find id %d", id);
2195 }
2196
2197 /*
2198 * Return TRUE if "id" is in the list of JSON message IDs we are waiting on.
2199 */
2200 static int
2201 channel_has_block_id(chanpart_T *chanpart, int id)
2202 {
2203 garray_T *gap = &chanpart->ch_block_ids;
2204 int i;
2205
2206 for (i = 0; i < gap->ga_len; ++i)
2207 if (((int *)gap->ga_data)[i] == id)
2208 return TRUE;
2209 return FALSE;
2210 }
2211
2212 /*
2157 * Get a message from the JSON queue for channel "channel". 2213 * Get a message from the JSON queue for channel "channel".
2158 * When "id" is positive it must match the first number in the list. 2214 * When "id" is positive it must match the first number in the list.
2159 * When "id" is zero or negative jut get the first message. But not the one 2215 * When "id" is zero or negative jut get the first message. But not one
2160 * with id ch_block_id. 2216 * in the ch_block_ids list.
2161 * When "without_callback" is TRUE also get messages that were pushed back. 2217 * When "without_callback" is TRUE also get messages that were pushed back.
2162 * Return OK when found and return the value in "rettv". 2218 * Return OK when found and return the value in "rettv".
2163 * Return FAIL otherwise. 2219 * Return FAIL otherwise.
2164 */ 2220 */
2165 static int 2221 static int
2180 2236
2181 if ((without_callback || !item->jq_no_callback) 2237 if ((without_callback || !item->jq_no_callback)
2182 && ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id) 2238 && ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
2183 || (id <= 0 && (tv->v_type != VAR_NUMBER 2239 || (id <= 0 && (tv->v_type != VAR_NUMBER
2184 || tv->vval.v_number == 0 2240 || tv->vval.v_number == 0
2185 || tv->vval.v_number != channel->ch_part[part].ch_block_id)))) 2241 || !channel_has_block_id(
2242 &channel->ch_part[part], tv->vval.v_number)))))
2186 { 2243 {
2187 *rettv = item->jq_value; 2244 *rettv = item->jq_value;
2188 if (tv->v_type == VAR_NUMBER) 2245 if (tv->v_type == VAR_NUMBER)
2189 ch_log(channel, "Getting JSON message %ld", 2246 ch_log(channel, "Getting JSON message %ld",
2190 (long)tv->vval.v_number); 2247 (long)tv->vval.v_number);
3048 free_tv(json_head->jq_next->jq_value); 3105 free_tv(json_head->jq_next->jq_value);
3049 remove_json_node(json_head, json_head->jq_next); 3106 remove_json_node(json_head, json_head->jq_next);
3050 } 3107 }
3051 3108
3052 free_callback(&ch_part->ch_callback); 3109 free_callback(&ch_part->ch_callback);
3110 ga_clear(&ch_part->ch_block_ids);
3053 3111
3054 while (ch_part->ch_writeque.wq_next != NULL) 3112 while (ch_part->ch_writeque.wq_next != NULL)
3055 remove_from_writeque(&ch_part->ch_writeque, 3113 remove_from_writeque(&ch_part->ch_writeque,
3056 ch_part->ch_writeque.wq_next); 3114 ch_part->ch_writeque.wq_next);
3057 } 3115 }
3478 /* 3536 /*
3479 * Read one JSON message with ID "id" from "channel"/"part" and store the 3537 * Read one JSON message with ID "id" from "channel"/"part" and store the
3480 * result in "rettv". 3538 * result in "rettv".
3481 * When "id" is -1 accept any message; 3539 * When "id" is -1 accept any message;
3482 * Blocks until the message is received or the timeout is reached. 3540 * Blocks until the message is received or the timeout is reached.
3541 * In corner cases this can be called recursively, that is why ch_block_ids is
3542 * a list.
3483 */ 3543 */
3484 static int 3544 static int
3485 channel_read_json_block( 3545 channel_read_json_block(
3486 channel_T *channel, 3546 channel_T *channel,
3487 ch_part_T part, 3547 ch_part_T part,
3492 int more; 3552 int more;
3493 sock_T fd; 3553 sock_T fd;
3494 int timeout; 3554 int timeout;
3495 chanpart_T *chanpart = &channel->ch_part[part]; 3555 chanpart_T *chanpart = &channel->ch_part[part];
3496 3556
3497 ch_log(channel, "Reading JSON"); 3557 ch_log(channel, "Blocking read JSON for id %d", id);
3498 if (id != -1) 3558 if (id >= 0)
3499 chanpart->ch_block_id = id; 3559 channel_add_block_id(chanpart, id);
3500 for (;;) 3560 for (;;)
3501 { 3561 {
3502 more = channel_parse_json(channel, part); 3562 more = channel_parse_json(channel, part);
3503 3563
3504 /* search for message "id" */ 3564 // search for message "id"
3505 if (channel_get_json(channel, part, id, TRUE, rettv) == OK) 3565 if (channel_get_json(channel, part, id, TRUE, rettv) == OK)
3506 { 3566 {
3507 chanpart->ch_block_id = 0; 3567 if (id >= 0)
3568 channel_remove_block_id(chanpart, id);
3569 ch_log(channel, "Received JSON for id %d", id);
3508 return OK; 3570 return OK;
3509 } 3571 }
3510 3572
3511 if (!more) 3573 if (!more)
3512 { 3574 {
3549 || channel_wait(channel, fd, timeout) != CW_READY) 3611 || channel_wait(channel, fd, timeout) != CW_READY)
3550 { 3612 {
3551 if (timeout == timeout_arg) 3613 if (timeout == timeout_arg)
3552 { 3614 {
3553 if (fd != INVALID_FD) 3615 if (fd != INVALID_FD)
3554 ch_log(channel, "Timed out"); 3616 ch_log(channel, "Timed out on id %d", id);
3555 break; 3617 break;
3556 } 3618 }
3557 } 3619 }
3558 else 3620 else
3559 channel_read(channel, part, "channel_read_json_block"); 3621 channel_read(channel, part, "channel_read_json_block");
3560 } 3622 }
3561 } 3623 }
3562 chanpart->ch_block_id = 0; 3624 if (id >= 0)
3625 channel_remove_block_id(chanpart, id);
3563 return FAIL; 3626 return FAIL;
3564 } 3627 }
3565 3628
3566 /* 3629 /*
3567 * Common for ch_read() and ch_readraw(). 3630 * Common for ch_read() and ch_readraw().