Mercurial > vim
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(). |