# HG changeset patch # User Bram Moolenaar # Date 1640807103 -3600 # Node ID 3c1dcb63f5793b32333b4769c2619c5505ef59b6 # Parent 6dccddda8bbcec931d3fb775fb898358967bf396 patch 8.2.3941: SIGTSTP is not handled Commit: https://github.com/vim/vim/commit/ab16ad33ba10dd12ff6660fa57b88f1a30ddd8ba Author: dbivolaru 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) diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt --- 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 diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- 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) { /* diff --git a/src/os_unix.c b/src/os_unix.c --- 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 } diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro --- 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); diff --git a/src/testdir/test_signals.vim b/src/testdir/test_signals.vim --- 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... diff --git a/src/version.c b/src/version.c --- 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,