mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-28 13:53:10 +00:00
remove unnecessary signal management
The shell was doing a log of signal blocking/unblocking that hurts performance and can be avoided. This reduced the elapsed time for a simple benchmark by 25%. Partial fix for #2007
This commit is contained in:
parent
51adf815aa
commit
fd6d814ea4
8 changed files with 113 additions and 85 deletions
|
@ -500,15 +500,23 @@ __sentinel bool contains_internal(const wcstring &needle, int vararg_handle, ...
|
||||||
}
|
}
|
||||||
|
|
||||||
long read_blocked(int fd, void *buf, size_t count) {
|
long read_blocked(int fd, void *buf, size_t count) {
|
||||||
ssize_t res;
|
long bytes_read = 0;
|
||||||
sigset_t chldset, oldset;
|
|
||||||
|
|
||||||
sigemptyset(&chldset);
|
while (count) {
|
||||||
sigaddset(&chldset, SIGCHLD);
|
ssize_t res = read(fd, (char *)buf + bytes_read, count);
|
||||||
VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &chldset, &oldset));
|
if (res == 0) {
|
||||||
res = read(fd, buf, count);
|
break;
|
||||||
VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &oldset, NULL));
|
} else if (res == -1) {
|
||||||
return res;
|
if (errno == EINTR) continue;
|
||||||
|
if (errno == EAGAIN) return bytes_read ? bytes_read : -1;
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
bytes_read += res;
|
||||||
|
count -= res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loop a write request while failure is non-critical. Return -1 and set errno in case of critical
|
/// Loop a write request while failure is non-critical. Return -1 and set errno in case of critical
|
||||||
|
|
|
@ -260,6 +260,8 @@ extern bool has_working_tty_timestamps;
|
||||||
|
|
||||||
/// Check if signals are blocked. If so, print an error message and return from the function
|
/// Check if signals are blocked. If so, print an error message and return from the function
|
||||||
/// performing this check.
|
/// performing this check.
|
||||||
|
#define CHECK_BLOCK(retval)
|
||||||
|
#if 0
|
||||||
#define CHECK_BLOCK(retval) \
|
#define CHECK_BLOCK(retval) \
|
||||||
if (signal_is_blocked()) { \
|
if (signal_is_blocked()) { \
|
||||||
debug(0, "function %s called while blocking signals. ", __func__); \
|
debug(0, "function %s called while blocking signals. ", __func__); \
|
||||||
|
@ -267,6 +269,7 @@ extern bool has_working_tty_timestamps;
|
||||||
show_stackframe(L'E'); \
|
show_stackframe(L'E'); \
|
||||||
return retval; \
|
return retval; \
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Shorthand for wgettext call in situations where a C-style string is needed (e.g., fwprintf()).
|
/// Shorthand for wgettext call in situations where a C-style string is needed (e.g., fwprintf()).
|
||||||
#define _(wstr) wgettext(wstr).c_str()
|
#define _(wstr) wgettext(wstr).c_str()
|
||||||
|
|
|
@ -367,12 +367,10 @@ static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *proce
|
||||||
|
|
||||||
void exec_job(parser_t &parser, job_t *j) {
|
void exec_job(parser_t &parser, job_t *j) {
|
||||||
pid_t pid = 0;
|
pid_t pid = 0;
|
||||||
sigset_t chldset;
|
|
||||||
|
|
||||||
// Set to true if something goes wrong while exec:ing the job, in which case the cleanup code
|
// Set to true if something goes wrong while exec:ing the job, in which case the cleanup code
|
||||||
// will kick in.
|
// will kick in.
|
||||||
bool exec_error = false;
|
bool exec_error = false;
|
||||||
|
|
||||||
bool needs_keepalive = false;
|
bool needs_keepalive = false;
|
||||||
process_t keepalive;
|
process_t keepalive;
|
||||||
|
|
||||||
|
@ -384,9 +382,6 @@ void exec_job(parser_t &parser, job_t *j) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sigemptyset(&chldset);
|
|
||||||
sigaddset(&chldset, SIGCHLD);
|
|
||||||
|
|
||||||
debug(4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id);
|
debug(4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id);
|
||||||
|
|
||||||
// Verify that all IO_BUFFERs are output. We used to support a (single, hacked-in) magical input
|
// Verify that all IO_BUFFERs are output. We used to support a (single, hacked-in) magical input
|
||||||
|
|
|
@ -392,6 +392,11 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
const io_chain_t empty_ios;
|
const io_chain_t empty_ios;
|
||||||
if (read_init(paths)) {
|
if (read_init(paths)) {
|
||||||
|
// TODO: Remove this once we're confident that not blocking/unblocking every signal around
|
||||||
|
// some critical sections is no longer necessary.
|
||||||
|
env_var_t fish_no_signal_block = env_get_string(L"FISH_NO_SIGNAL_BLOCK");
|
||||||
|
if (!fish_no_signal_block.missing()) ignore_signal_block = true;
|
||||||
|
|
||||||
// Stomp the exit status of any initialization commands (issue #635).
|
// Stomp the exit status of any initialization commands (issue #635).
|
||||||
proc_set_last_status(STATUS_BUILTIN_OK);
|
proc_set_last_status(STATUS_BUILTIN_OK);
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,11 @@ bool set_child_group(job_t *j, process_t *p, int print_errors) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND)) { //!OCLINT(early exit)
|
if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND)) { //!OCLINT(early exit)
|
||||||
int result = tcsetpgrp(STDIN_FILENO, j->pgid); // to avoid "collapsible if statements" warn
|
int result = -1;
|
||||||
|
errno = EINTR;
|
||||||
|
while (result == -1 && errno == EINTR) {
|
||||||
|
result = tcsetpgrp(STDIN_FILENO, j->pgid);
|
||||||
|
}
|
||||||
if (result == -1) {
|
if (result == -1) {
|
||||||
if (errno == ENOTTY) redirect_tty_output();
|
if (errno == ENOTTY) redirect_tty_output();
|
||||||
if (print_errors) {
|
if (print_errors) {
|
||||||
|
|
19
src/proc.cpp
19
src/proc.cpp
|
@ -792,19 +792,32 @@ static void read_try(job_t *j) {
|
||||||
/// \param cont If this variable is set, we are giving back control to a job that has previously
|
/// \param cont If this variable is set, we are giving back control to a job that has previously
|
||||||
/// been stopped. In that case, we need to set the terminal attributes to those saved in the job.
|
/// been stopped. In that case, we need to set the terminal attributes to those saved in the job.
|
||||||
static bool terminal_give_to_job(job_t *j, int cont) {
|
static bool terminal_give_to_job(job_t *j, int cont) {
|
||||||
if (tcsetpgrp(STDIN_FILENO, j->pgid) == -1) {
|
int result = -1;
|
||||||
|
errno = EINTR;
|
||||||
|
while (result == -1 && errno == EINTR) {
|
||||||
|
result = tcsetpgrp(STDIN_FILENO, j->pgid);
|
||||||
|
}
|
||||||
|
if (result == -1) {
|
||||||
if (errno == ENOTTY) redirect_tty_output();
|
if (errno == ENOTTY) redirect_tty_output();
|
||||||
debug(1, _(L"Could not send job %d ('%ls') to foreground"), j->job_id, j->command_wcstr());
|
debug(1, _(L"Could not send job %d ('%ls') to foreground"), j->job_id, j->command_wcstr());
|
||||||
wperror(L"tcsetpgrp");
|
wperror(L"tcsetpgrp");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cont && tcsetattr(STDIN_FILENO, TCSADRAIN, &j->tmodes)) {
|
if (cont) {
|
||||||
|
int result = -1;
|
||||||
|
errno = EINTR;
|
||||||
|
while (result == -1 && errno == EINTR) {
|
||||||
|
result = tcsetattr(STDIN_FILENO, TCSADRAIN, &j->tmodes);
|
||||||
|
}
|
||||||
|
if (result == -1) {
|
||||||
if (errno == ENOTTY) redirect_tty_output();
|
if (errno == ENOTTY) redirect_tty_output();
|
||||||
debug(1, _(L"Could not send job %d ('%ls') to foreground"), j->job_id, j->command_wcstr());
|
debug(1, _(L"terminal_give_to_job(): Could not send job %d ('%ls') to foreground"), j->job_id, j->command_wcstr());
|
||||||
wperror(L"tcsetattr");
|
wperror(L"tcsetattr");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
113
src/signal.cpp
113
src/signal.cpp
|
@ -16,6 +16,9 @@
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
#include "wutil.h" // IWYU pragma: keep
|
#include "wutil.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
// This is a temporary var while we explore whether signal_block() and friends is needed.
|
||||||
|
bool ignore_signal_block = false;
|
||||||
|
|
||||||
/// Struct describing an entry for the lookup table used to convert between signal names and signal
|
/// Struct describing an entry for the lookup table used to convert between signal names and signal
|
||||||
/// ids, etc.
|
/// ids, etc.
|
||||||
struct lookup_entry {
|
struct lookup_entry {
|
||||||
|
@ -252,88 +255,77 @@ void signal_reset_handlers() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets appropriate signal handlers.
|
static void set_interactive_handlers() {
|
||||||
void signal_set_handlers() {
|
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
|
||||||
sigemptyset(&act.sa_mask);
|
sigemptyset(&act.sa_mask);
|
||||||
act.sa_flags = SA_SIGINFO;
|
|
||||||
act.sa_sigaction = &default_handler;
|
|
||||||
|
|
||||||
// First reset everything to a use default_handler, a function whose sole action is to fire of
|
|
||||||
// an event.
|
|
||||||
sigaction(SIGINT, &act, 0);
|
|
||||||
sigaction(SIGQUIT, &act, 0);
|
|
||||||
sigaction(SIGTSTP, &act, 0);
|
|
||||||
sigaction(SIGTTIN, &act, 0);
|
|
||||||
sigaction(SIGTTOU, &act, 0);
|
|
||||||
sigaction(SIGCHLD, &act, 0);
|
|
||||||
|
|
||||||
// Ignore sigpipe, which we may get from the universal variable notifier.
|
|
||||||
sigaction(SIGPIPE, &act, 0);
|
|
||||||
|
|
||||||
if (shell_is_interactive()) {
|
|
||||||
// Interactive mode. Ignore interactive signals. We are a shell, we know what is best for
|
// Interactive mode. Ignore interactive signals. We are a shell, we know what is best for
|
||||||
// the user.
|
// the user.
|
||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
|
|
||||||
sigaction(SIGINT, &act, 0);
|
sigaction(SIGINT, &act, 0);
|
||||||
sigaction(SIGQUIT, &act, 0);
|
sigaction(SIGQUIT, &act, 0);
|
||||||
sigaction(SIGTSTP, &act, 0);
|
sigaction(SIGTSTP, &act, 0);
|
||||||
sigaction(SIGTTIN, &act, 0);
|
|
||||||
sigaction(SIGTTOU, &act, 0);
|
sigaction(SIGTTOU, &act, 0);
|
||||||
|
|
||||||
|
// We don't ignore SIGTTIN because we might send it to ourself.
|
||||||
|
act.sa_sigaction = &default_handler;
|
||||||
|
act.sa_flags = SA_SIGINFO;
|
||||||
|
sigaction(SIGTTIN, &act, 0);
|
||||||
|
|
||||||
act.sa_sigaction = &handle_int;
|
act.sa_sigaction = &handle_int;
|
||||||
act.sa_flags = SA_SIGINFO;
|
act.sa_flags = SA_SIGINFO;
|
||||||
if (sigaction(SIGINT, &act, 0)) {
|
sigaction(SIGINT, &act, 0);
|
||||||
wperror(L"sigaction");
|
|
||||||
FATAL_EXIT();
|
|
||||||
}
|
|
||||||
|
|
||||||
act.sa_sigaction = &handle_chld;
|
|
||||||
act.sa_flags = SA_SIGINFO;
|
|
||||||
if (sigaction(SIGCHLD, &act, 0)) {
|
|
||||||
wperror(L"sigaction");
|
|
||||||
FATAL_EXIT();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SIGWINCH
|
|
||||||
act.sa_flags = SA_SIGINFO;
|
|
||||||
act.sa_sigaction = &handle_winch;
|
|
||||||
if (sigaction(SIGWINCH, &act, 0)) {
|
|
||||||
wperror(L"sigaction");
|
|
||||||
FATAL_EXIT();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
act.sa_flags = SA_SIGINFO;
|
|
||||||
act.sa_sigaction = &handle_hup;
|
|
||||||
if (sigaction(SIGHUP, &act, 0)) {
|
|
||||||
wperror(L"sigaction");
|
|
||||||
FATAL_EXIT();
|
|
||||||
}
|
|
||||||
|
|
||||||
// SIGTERM restores the terminal controlling process before dying.
|
// SIGTERM restores the terminal controlling process before dying.
|
||||||
act.sa_flags = SA_SIGINFO;
|
|
||||||
act.sa_sigaction = &handle_term;
|
act.sa_sigaction = &handle_term;
|
||||||
if (sigaction(SIGTERM, &act, 0)) {
|
act.sa_flags = SA_SIGINFO;
|
||||||
wperror(L"sigaction");
|
sigaction(SIGTERM, &act, 0);
|
||||||
FATAL_EXIT();
|
|
||||||
}
|
act.sa_sigaction = &handle_hup;
|
||||||
} else {
|
act.sa_flags = SA_SIGINFO;
|
||||||
|
sigaction(SIGHUP, &act, 0);
|
||||||
|
|
||||||
|
#ifdef SIGWINCH
|
||||||
|
act.sa_sigaction = &handle_winch;
|
||||||
|
act.sa_flags = SA_SIGINFO;
|
||||||
|
sigaction(SIGWINCH, &act, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_non_interactive_handlers() {
|
||||||
|
struct sigaction act;
|
||||||
|
sigemptyset(&act.sa_mask);
|
||||||
|
|
||||||
// Non-interactive. Ignore interrupt, check exit status of processes to determine result
|
// Non-interactive. Ignore interrupt, check exit status of processes to determine result
|
||||||
// instead.
|
// instead.
|
||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
sigaction(SIGINT, &act, 0);
|
sigaction(SIGINT, &act, 0);
|
||||||
sigaction(SIGQUIT, &act, 0);
|
sigaction(SIGQUIT, &act, 0);
|
||||||
|
}
|
||||||
|
|
||||||
act.sa_handler = SIG_DFL;
|
/// Sets up appropriate signal handlers.
|
||||||
act.sa_sigaction = &handle_chld;
|
void signal_set_handlers() {
|
||||||
|
struct sigaction act;
|
||||||
|
sigemptyset(&act.sa_mask);
|
||||||
|
|
||||||
|
// Ignore SIGPIPE. We'll detect failed writes and deal with them appropriately. We don't want
|
||||||
|
// this signal interrupting other syscalls or terminating us.
|
||||||
|
act.sa_handler = SIG_IGN;
|
||||||
|
sigaction(SIGPIPE, &act, 0);
|
||||||
|
|
||||||
|
// Whether or not we're interactive we want SIGCHLD to not interrupt restartable syscalls.
|
||||||
act.sa_flags = SA_SIGINFO;
|
act.sa_flags = SA_SIGINFO;
|
||||||
|
act.sa_sigaction = &handle_chld;
|
||||||
|
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||||
if (sigaction(SIGCHLD, &act, 0)) {
|
if (sigaction(SIGCHLD, &act, 0)) {
|
||||||
wperror(L"sigaction");
|
wperror(L"sigaction");
|
||||||
exit_without_destructors(1);
|
FATAL_EXIT();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shell_is_interactive()) {
|
||||||
|
set_interactive_handlers();
|
||||||
|
} else {
|
||||||
|
set_non_interactive_handlers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,6 +359,8 @@ void get_signals_with_handlers(sigset_t *set) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_block() {
|
void signal_block() {
|
||||||
|
if (ignore_signal_block) return;
|
||||||
|
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
sigset_t chldset;
|
sigset_t chldset;
|
||||||
|
|
||||||
|
@ -380,6 +374,8 @@ void signal_block() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_unblock() {
|
void signal_unblock() {
|
||||||
|
if (ignore_signal_block) return;
|
||||||
|
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
sigset_t chldset;
|
sigset_t chldset;
|
||||||
|
|
||||||
|
@ -398,4 +394,7 @@ void signal_unblock() {
|
||||||
// debug( 0, L"signal block level decreased to %d", block_count );
|
// debug( 0, L"signal block level decreased to %d", block_count );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signal_is_blocked() { return static_cast<bool>(block_count); }
|
bool signal_is_blocked() {
|
||||||
|
if (ignore_signal_block) return false;
|
||||||
|
return static_cast<bool>(block_count);
|
||||||
|
}
|
||||||
|
|
|
@ -38,4 +38,5 @@ bool signal_is_blocked();
|
||||||
/// Returns signals with non-default handlers.
|
/// Returns signals with non-default handlers.
|
||||||
void get_signals_with_handlers(sigset_t *set);
|
void get_signals_with_handlers(sigset_t *set);
|
||||||
|
|
||||||
|
extern bool ignore_signal_block;
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue