mirror of
https://github.com/swaywm/sway.git
synced 2026-04-21 06:46:22 -04:00
Rework fork/exec strategy
cmd_exec_process is used whenever sway is meant to execute a child process on behalf of the user, and had a lot of complexity. In order to avoid having to wait on the user's process, a double-fork was used, which in turn required us to wait on the outer process. In order to track the child PID for launcher purposes, a pipe was used to transmit the PID back to sway. This resulted in sway blocking for 5-6 ms per exec on my system, which is quite significant. The error handling was also quite lacking - the read loop did not handle errors at all for example. Instead, teach sway to handle SIGCHLD and do away with the double-fork. This in turn allows us to get rid of the pipe as we can record the child's PID directly. This reduces the time we block to just 1.5 ms on my system. We'd be able to get down to just 150 µs if we could use posix_spawn(3), but posix_spawn(3) cannot reset NOFILE. clone(2) or vfork(2) would be alternatives, but that presents portability issues. This change is replicated for swaybar, swaybg and swaynag handling, which had similar albeit less complicated implementations.
This commit is contained in:
parent
962e1e70a6
commit
e3d9cc2aa5
5 changed files with 73 additions and 140 deletions
|
|
@ -25,16 +25,6 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
|
|||
return error;
|
||||
}
|
||||
|
||||
static void export_xdga_token(struct launcher_ctx *ctx) {
|
||||
const char *token = launcher_ctx_get_token_name(ctx);
|
||||
setenv("XDG_ACTIVATION_TOKEN", token, 1);
|
||||
}
|
||||
|
||||
static void export_startup_id(struct launcher_ctx *ctx) {
|
||||
const char *token = launcher_ctx_get_token_name(ctx);
|
||||
setenv("DESKTOP_STARTUP_ID", token, 1);
|
||||
}
|
||||
|
||||
struct cmd_results *cmd_exec_process(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
char *cmd = NULL;
|
||||
|
|
@ -56,67 +46,42 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
|
|||
|
||||
sway_log(SWAY_DEBUG, "Executing %s", cmd);
|
||||
|
||||
int fd[2];
|
||||
if (pipe(fd) != 0) {
|
||||
sway_log(SWAY_ERROR, "Unable to create pipe for fork");
|
||||
}
|
||||
|
||||
pid_t pid, child;
|
||||
struct launcher_ctx *ctx = launcher_ctx_create_internal();
|
||||
|
||||
// Fork process
|
||||
if ((pid = fork()) == 0) {
|
||||
// Fork child process again
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
restore_nofile_limit();
|
||||
setsid();
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
close(fd[0]);
|
||||
if ((child = fork()) == 0) {
|
||||
close(fd[1]);
|
||||
if (ctx) {
|
||||
export_xdga_token(ctx);
|
||||
|
||||
if (ctx) {
|
||||
const char *token = launcher_ctx_get_token_name(ctx);
|
||||
setenv("XDG_ACTIVATION_TOKEN", token, 1);
|
||||
if (!no_startup_id) {
|
||||
setenv("DESKTOP_STARTUP_ID", token, 1);
|
||||
}
|
||||
if (ctx && !no_startup_id) {
|
||||
export_startup_id(ctx);
|
||||
}
|
||||
execlp("sh", "sh", "-c", cmd, (void *)NULL);
|
||||
sway_log_errno(SWAY_ERROR, "execlp failed");
|
||||
_exit(1);
|
||||
}
|
||||
ssize_t s = 0;
|
||||
while ((size_t)s < sizeof(pid_t)) {
|
||||
s += write(fd[1], ((uint8_t *)&child) + s, sizeof(pid_t) - s);
|
||||
}
|
||||
close(fd[1]);
|
||||
|
||||
execlp("sh", "sh", "-c", cmd, (void*)NULL);
|
||||
sway_log_errno(SWAY_ERROR, "execve failed");
|
||||
_exit(0); // Close child process
|
||||
} else if (pid < 0) {
|
||||
} else if (child < 0) {
|
||||
launcher_ctx_destroy(ctx);
|
||||
free(cmd);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
return cmd_results_new(CMD_FAILURE, "fork() failed");
|
||||
}
|
||||
free(cmd);
|
||||
close(fd[1]); // close write
|
||||
ssize_t s = 0;
|
||||
while ((size_t)s < sizeof(pid_t)) {
|
||||
s += read(fd[0], ((uint8_t *)&child) + s, sizeof(pid_t) - s);
|
||||
}
|
||||
close(fd[0]);
|
||||
// cleanup child process
|
||||
waitpid(pid, NULL, 0);
|
||||
if (child > 0) {
|
||||
sway_log(SWAY_DEBUG, "Child process created with pid %d", child);
|
||||
if (ctx != NULL) {
|
||||
sway_log(SWAY_DEBUG, "Recording workspace for process %d", child);
|
||||
ctx->pid = child;
|
||||
}
|
||||
} else {
|
||||
launcher_ctx_destroy(ctx);
|
||||
return cmd_results_new(CMD_FAILURE, "Second fork() failed");
|
||||
|
||||
sway_log(SWAY_DEBUG, "Child process created with pid %d", child);
|
||||
if (ctx != NULL) {
|
||||
sway_log(SWAY_DEBUG, "Recording workspace for process %d", child);
|
||||
ctx->pid = child;
|
||||
}
|
||||
|
||||
free(cmd);
|
||||
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue