changeset 26217:d25c0b0aad7d v8.2.3640

patch 8.2.3640: freeze when calling term_wait() in a close callback Commit: https://github.com/vim/vim/commit/eea32afdb83ae281a63152f7494f79ec7e45ff55 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Nov 21 14:51:13 2021 +0000 patch 8.2.3640: freeze when calling term_wait() in a close callback Problem: Freeze when calling term_wait() in a close callback. Solution: Set a "closing" flag to tell term_wait() to return. (closes https://github.com/vim/vim/issues/9152)
author Bram Moolenaar <Bram@vim.org>
date Sun, 21 Nov 2021 16:00:04 +0100
parents 285b3f4de8a9
children 5bf41f09e0e1
files src/channel.c src/proto/terminal.pro src/terminal.c src/testdir/test_terminal.vim src/version.c
diffstat 5 files changed, 42 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -3156,6 +3156,10 @@ channel_close(channel_T *channel, int in
     {
 	ch_part_T	part;
 
+#ifdef FEAT_TERMINAL
+	// let the terminal know it is closing to avoid getting stuck
+	term_channel_closing(channel);
+#endif
 	// Invoke callbacks and flush buffers before the close callback.
 	if (channel->ch_close_cb.cb_name != NULL)
 	    ch_log(channel,
--- a/src/proto/terminal.pro
+++ b/src/proto/terminal.pro
@@ -20,6 +20,7 @@ int term_use_loop(void);
 void term_win_entered(void);
 int terminal_loop(int blocking);
 int may_close_term_popup(void);
+void term_channel_closing(channel_T *ch);
 void term_channel_closed(channel_T *ch);
 void term_check_channel_closed_recently(void);
 int term_do_update_window(win_T *wp);
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -99,6 +99,7 @@ struct terminal_S {
     int		tl_vterm_size_changed;
 
     int		tl_normal_mode; // TRUE: Terminal-Normal mode
+    int		tl_channel_closing;
     int		tl_channel_closed;
     int		tl_channel_recently_closed; // still need to handle tl_finish
 
@@ -3459,6 +3460,20 @@ may_close_term_popup(void)
 #endif
 
 /*
+ * Called when a channel is going to be closed, before invoking the close
+ * callback.
+ */
+    void
+term_channel_closing(channel_T *ch)
+{
+    term_T *term;
+
+    for (term = first_term; term != NULL; term = term->tl_next)
+	if (term->tl_job == ch->ch_job && !term->tl_channel_closed)
+	    term->tl_channel_closing = TRUE;
+}
+
+/*
  * Called when a channel has been closed.
  * If this was a channel for a terminal window then finish it up.
  */
@@ -6438,6 +6453,9 @@ f_term_wait(typval_T *argvars, typval_T 
 		// If the terminal is closed when the channel is closed the
 		// buffer disappears.
 		break;
+	    if (buf->b_term == NULL || buf->b_term->tl_channel_closing)
+		// came here from a close callback, only wait one time
+		break;
 	}
 
 	term_flush_messages();
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -2058,5 +2058,22 @@ func Test_terminal_adds_jump()
   bwipe!
 endfunc
 
+func Close_cb(ch, ctx)
+  call term_wait(a:ctx.bufnr)
+  let g:close_done = 'done'
+endfunc
+
+func Test_term_wait_in_close_cb()
+  let g:close_done = ''
+  let ctx = {}
+  let ctx.bufnr = term_start('echo "HELLO WORLD"',
+        \ {'close_cb': {ch -> Close_cb(ch, ctx)}})
+
+  call WaitForAssert({-> assert_equal("done", g:close_done)})
+
+  unlet g:close_done
+  bwipe!
+endfunc
+
 
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3640,
+/**/
     3639,
 /**/
     3638,