summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-11-21 14:51:13 +0000
committerBram Moolenaar <Bram@vim.org>2021-11-21 14:51:13 +0000
commiteea32afdb83ae281a63152f7494f79ec7e45ff55 (patch)
tree78938a8c7f2d263f1d66a3c6293ea4c9af8ef3a0
parent7f0c4b418e01d6e056a74de2f22fcbad613b7591 (diff)
downloadvim-git-eea32afdb83ae281a63152f7494f79ec7e45ff55.tar.gz
patch 8.2.3640: freeze when calling term_wait() in a close callbackv8.2.3640
Problem: Freeze when calling term_wait() in a close callback. Solution: Set a "closing" flag to tell term_wait() to return. (closes #9152)
-rw-r--r--src/channel.c4
-rw-r--r--src/proto/terminal.pro1
-rw-r--r--src/terminal.c18
-rw-r--r--src/testdir/test_terminal.vim17
-rw-r--r--src/version.c2
5 files changed, 42 insertions, 0 deletions
diff --git a/src/channel.c b/src/channel.c
index b46205e67..7514d6385 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -3156,6 +3156,10 @@ channel_close(channel_T *channel, int invoke_close_cb)
{
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,
diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro
index 930460671..f5bd1a6c7 100644
--- 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);
diff --git a/src/terminal.c b/src/terminal.c
index 24779d9c2..f6c283b63 100644
--- 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 *rettv UNUSED)
// 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();
diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim
index 62dfbf105..8cdb42ae8 100644
--- 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
diff --git a/src/version.c b/src/version.c
index 6162d871d..a83ff61bc 100644
--- 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,