To: vim_dev@googlegroups.com Subject: Patch 8.1.1512 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@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 ///