diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/event/loop.c | 5 | ||||
-rw-r--r-- | src/nvim/event/multiqueue.c | 10 | ||||
-rw-r--r-- | src/nvim/terminal.c | 19 |
3 files changed, 27 insertions, 7 deletions
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 15d993cc62..f8b149ec78 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -212,10 +212,7 @@ static void async_cb(uv_async_t *handle) Loop *l = handle->loop->data; uv_mutex_lock(&l->mutex); // Flush thread_events to fast_events for processing on main loop. - while (!multiqueue_empty(l->thread_events)) { - Event ev = multiqueue_get(l->thread_events); - multiqueue_put_event(l->fast_events, ev); - } + multiqueue_move_events(l->fast_events, l->thread_events); uv_mutex_unlock(&l->mutex); } diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index 8646173776..5072bd774d 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -140,6 +140,16 @@ void multiqueue_put_event(MultiQueue *self, Event event) } } +/// Move events from src to dest. +void multiqueue_move_events(MultiQueue *dest, MultiQueue *src) + FUNC_ATTR_NONNULL_ALL +{ + while (!multiqueue_empty(src)) { + Event event = multiqueue_get(src); + multiqueue_put_event(dest, event); + } +} + void multiqueue_process_events(MultiQueue *self) { assert(self); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 8300b536e2..c0f938adda 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -178,6 +178,7 @@ struct terminal { bool resize; ///< pending width/height bool cursor; ///< pending cursor shape or blink change StringBuilder *send; ///< When there is a pending TermRequest autocommand, block and store input. + MultiQueue *events; ///< Events waiting for refresh. } pending; bool theme_updates; ///< Send a theme update notification when 'bg' changes @@ -222,9 +223,10 @@ static void emit_termrequest(void **argv) if (term->sb_pending > 0) { // Don't emit the event while there is pending scrollback because we need - // the buffer contents to be fully updated. If this is the case, re-schedule - // the event. - multiqueue_put(main_loop.events, emit_termrequest, term, sequence, (void *)sequence_length, + // the buffer contents to be fully updated. If this is the case, schedule + // the event onto the pending queue where it will be executed after the + // terminal is refreshed and the pending scrollback is cleared. + multiqueue_put(term->pending.events, emit_termrequest, term, sequence, (void *)sequence_length, pending_send, (void *)(intptr_t)row, (void *)(intptr_t)col); return; } @@ -490,6 +492,13 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) term->invalid_start = 0; term->invalid_end = opts.height; + // Create a separate queue for events which need to wait for a terminal + // refresh. We cannot reschedule events back onto the main queue because this + // can create an infinite loop (#32753). + // This queue is never processed directly: when the terminal is refreshed, all + // events from this queue are copied back onto the main event queue. + term->pending.events = multiqueue_new_parent(NULL, NULL); + aco_save_T aco; aucmd_prepbuf(&aco, buf); @@ -972,6 +981,7 @@ void terminal_destroy(Terminal **termpp) kv_destroy(term->selection); kv_destroy(term->termrequest_buffer); vterm_free(term->vt); + multiqueue_free(term->pending.events); xfree(term); *termpp = NULL; // coverity[dead-store] } @@ -2029,6 +2039,9 @@ static void refresh_terminal(Terminal *term) int ml_added = buf->b_ml.ml_line_count - ml_before; adjust_topline(term, buf, ml_added); + + // Copy pending events back to the main event queue + multiqueue_move_events(main_loop.events, term->pending.events); } static void refresh_cursor(Terminal *term, bool *cursor_visible) |