changeset 26825:3c1dcb63f579 v8.2.3941

patch 8.2.3941: SIGTSTP is not handled Commit: https://github.com/vim/vim/commit/ab16ad33ba10dd12ff6660fa57b88f1a30ddd8ba Author: dbivolaru <dbivolaru@jacobs-alumni.de> Date: Wed Dec 29 19:41:47 2021 +0000 patch 8.2.3941: SIGTSTP is not handled Problem: SIGTSTP is not handled. Solution: Handle SIGTSTP like pressing CTRL-Z. (closes https://github.com/vim/vim/issues/9422)
author Bram Moolenaar <Bram@vim.org>
date Wed, 29 Dec 2021 20:45:03 +0100
parents 6dccddda8bbc
children 4876191a71c0
files runtime/doc/autocmd.txt src/ex_docmd.c src/os_unix.c src/proto/ex_docmd.pro src/testdir/test_signals.vim src/version.c
diffstat 6 files changed, 94 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -1279,8 +1279,8 @@ VimResume			When the Vim instance is res
 	:autocmd VimResume * checktime
 <							*VimSuspend*
 VimSuspend			When the Vim instance is suspended.  Only when
-				CTRL-Z was typed inside Vim, not when the
-				SIGSTOP or SIGTSTP signal was sent to Vim.
+				CTRL-Z was typed inside Vim, or when the SIGTSTP
+				signal was sent to Vim, but not for SIGSTOP.
 							*WinClosed*
 WinClosed			After closing a window.  The pattern is
 				matched against the |window-ID|.  Both
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -109,7 +109,6 @@ static void	ex_pedit(exarg_T *eap);
 # define ex_pedit		ex_ni
 #endif
 static void	ex_hide(exarg_T *eap);
-static void	ex_stop(exarg_T *eap);
 static void	ex_exit(exarg_T *eap);
 static void	ex_print(exarg_T *eap);
 #ifdef FEAT_BYTEOFF
@@ -6200,7 +6199,7 @@ ex_hide(exarg_T *eap UNUSED)
 /*
  * ":stop" and ":suspend": Suspend Vim.
  */
-    static void
+    void
 ex_stop(exarg_T *eap)
 {
     /*
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -157,6 +157,11 @@ static void handle_resize(void);
 #if defined(SIGWINCH)
 static RETSIGTYPE sig_winch SIGPROTOARG;
 #endif
+#if defined(SIGTSTP)
+static RETSIGTYPE sig_tstp SIGPROTOARG;
+// volatile because it is used in signal handler sig_tstp() and sigcont_handler().
+static volatile sig_atomic_t in_mch_suspend = FALSE;
+#endif
 #if defined(SIGINT)
 static RETSIGTYPE catch_sigint SIGPROTOARG;
 #endif
@@ -197,6 +202,8 @@ static int save_patterns(int num_pat, ch
 
 // volatile because it is used in signal handler sig_winch().
 static volatile sig_atomic_t do_resize = FALSE;
+// volatile because it is used in signal handler sig_tstp().
+static volatile sig_atomic_t got_tstp = FALSE;
 static char_u	*extra_shell_arg = NULL;
 static int	show_shell_mess = TRUE;
 // volatile because it is used in signal handler deathtrap().
@@ -851,6 +858,24 @@ sig_winch SIGDEFARG(sigarg)
 }
 #endif
 
+#if defined(SIGTSTP)
+    static RETSIGTYPE
+sig_tstp SIGDEFARG(sigarg)
+{
+    // Second time we get called we actually need to suspend
+    if (in_mch_suspend)
+    {
+	signal(SIGTSTP, ignore_sigtstp ? SIG_IGN : SIG_DFL);
+	raise(sigarg);
+    }
+
+    // this is not required on all systems, but it doesn't hurt anybody
+    signal(SIGTSTP, (RETSIGTYPE (*)())sig_tstp);
+    got_tstp = TRUE;
+    SIGRETURN;
+}
+#endif
+
 #if defined(SIGINT)
     static RETSIGTYPE
 catch_sigint SIGDEFARG(sigarg)
@@ -1158,7 +1183,6 @@ after_sigcont(void)
 
 #if defined(SIGCONT)
 static RETSIGTYPE sigcont_handler SIGPROTOARG;
-static volatile sig_atomic_t in_mch_suspend = FALSE;
 
 /*
  * With multi-threading, suspending might not work immediately.  Catch the
@@ -1353,7 +1377,7 @@ set_signals(void)
 
 #ifdef SIGTSTP
     // See mch_init() for the conditions under which we ignore SIGTSTP.
-    signal(SIGTSTP, ignore_sigtstp ? SIG_IGN : SIG_DFL);
+    signal(SIGTSTP, ignore_sigtstp ? SIG_IGN : (RETSIGTYPE (*)())sig_tstp);
 #endif
 #if defined(SIGCONT)
     signal(SIGCONT, sigcont_handler);
@@ -6386,6 +6410,15 @@ select_eintr:
 # ifdef EINTR
 	if (ret == -1 && errno == EINTR)
 	{
+	    // Check whether the EINTR is caused by SIGTSTP
+	    if (got_tstp && !in_mch_suspend)
+	    {
+		exarg_T ea;
+		ea.forceit = TRUE;
+		ex_stop(&ea);
+		got_tstp = FALSE;
+	    }
+
 	    // Check whether window has been resized, EINTR may be caused by
 	    // SIGWINCH.
 	    if (do_resize)
@@ -7176,7 +7209,7 @@ gpm_open(void)
 	    // we are going to suspend or starting an external process
 	    // so we shouldn't  have problem with this
 # ifdef SIGTSTP
-	    signal(SIGTSTP, restricted ? SIG_IGN : SIG_DFL);
+	    signal(SIGTSTP, restricted ? SIG_IGN : (RETSIGTYPE (*)())sig_tstp);
 # endif
 	    return 1; // succeed
 	}
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -40,6 +40,7 @@ int before_quit_autocmds(win_T *wp, int 
 void ex_quit(exarg_T *eap);
 void tabpage_close(int forceit);
 void tabpage_close_other(tabpage_T *tp, int forceit);
+void ex_stop(exarg_T *eap);
 void handle_drop(int filec, char_u **filev, int split, void (*callback)(void *), void *cookie);
 void handle_any_postponed_drop(void);
 void ex_splitview(exarg_T *eap);
--- a/src/testdir/test_signals.vim
+++ b/src/testdir/test_signals.vim
@@ -105,6 +105,58 @@ func Test_signal_INT()
   call StopVimInTerminal(buf)
 endfunc
 
+" Test signal TSTP. Handler sets got_tstp.
+func Test_signal_TSTP()
+  CheckRunVimInTerminal
+  if !HasSignal('TSTP')
+    throw 'Skipped: TSTP signal not supported'
+  endif
+
+  " Skip the test when running with valgrind as signal TSTP is not received
+  " somehow by Vim when running with valgrind.
+  let cmd = GetVimCommand()
+  if cmd =~ 'valgrind'
+    throw 'Skipped: cannot test signal TSTP with valgrind'
+  endif
+
+  " If test fails once, it can leave temporary files and trying to rerun
+  " the test would then fail again if they are not deleted first.
+  call delete('.Xsig_TERM.swp')
+  call delete('XsetupAucmd')
+  call delete('XautoOut')
+  let lines =<< trim END
+    au VimSuspend * call writefile(["VimSuspend triggered"], "XautoOut", "as")
+    au VimResume * call writefile(["VimResume triggered"], "XautoOut", "as")
+  END
+  call writefile(lines, 'XsetupAucmd')
+
+  let buf = RunVimInTerminal('-S XsetupAucmd Xsig_TERM', {'rows': 6})
+  let pid_vim = term_getjob(buf)->job_info().process
+
+  call term_sendkeys(buf, ":call setline(1, 'foo')\n")
+  call WaitForAssert({-> assert_equal('foo', term_getline(buf, 1))})
+
+  call assert_false(filereadable('Xsig_TERM'))
+
+  " After TSTP the file is not saved (same function as ^Z)
+  exe 'silent !kill -s TSTP ' .. pid_vim
+  call WaitForAssert({-> assert_true(filereadable('.Xsig_TERM.swp'))})
+
+  " We resume after the suspend
+  exe 'silent !kill -s CONT ' .. pid_vim
+  exe 'silent !sleep 0.006'
+
+  call StopVimInTerminal(buf)
+
+  let result = readfile('XautoOut')
+  call assert_equal(["VimSuspend triggered", "VimResume triggered"], result)
+
+  %bwipe!
+  call delete('.Xsig_TERM.swp')
+  call delete('XsetupAucmd')
+  call delete('XautoOut')
+endfunc
+
 " Test a deadly signal.
 "
 " There are several deadly signals: SISEGV, SIBUS, SIGTERM...
--- a/src/version.c
+++ b/src/version.c
@@ -750,6 +750,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3941,
+/**/
     3940,
 /**/
     3939,