]> granicus.if.org Git - vim/commitdiff
patch 8.2.3640: freeze when calling term_wait() in a close callback v8.2.3640
authorBram Moolenaar <Bram@vim.org>
Sun, 21 Nov 2021 14:51:13 +0000 (14:51 +0000)
committerBram Moolenaar <Bram@vim.org>
Sun, 21 Nov 2021 14:51:13 +0000 (14:51 +0000)
Problem:    Freeze when calling term_wait() in a close callback.
Solution:   Set a "closing" flag to tell term_wait() to return. (closes #9152)

src/channel.c
src/proto/terminal.pro
src/terminal.c
src/testdir/test_terminal.vim
src/version.c

index b46205e67534774c8c5f69dfee6c814532d7269f..7514d638557e5dd4867c03e2a98c71711126c893 100644 (file)
@@ -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,
index 930460671b18fa155278eaabd38fa218333c0029..f5bd1a6c7c5203865a15a936753d62eaf1aa6f48 100644 (file)
@@ -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);
index 24779d9c2d43f73602dfc898f8ef9a483ccc5144..f6c283b639455e711829168240876bac1c72f80f 100644 (file)
@@ -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
 
@@ -3458,6 +3459,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();
index 62dfbf1050fb19675f4f2d85d34f187bf9b733de..8cdb42ae8877cfb80c70bb8714882412ec8d208c 100644 (file)
@@ -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
index 6162d871d0f27a56ffbb36576b1fd89afc76b788..a83ff61bca0c3e697824ab62134f1dd0ba9a51fe 100644 (file)
@@ -757,6 +757,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3640,
 /**/
     3639,
 /**/