Commit graph

7365 commits

Author SHA1 Message Date
Mahmoud Al-Qudsi
5db8065f15 unblock_previous on exec_job finish 2017-08-06 14:40:18 -07:00
Mahmoud Al-Qudsi
c3d756b5df blocking only if pipes_to_next_command breaks things like read.expect test 2017-08-06 14:40:18 -07:00
Mahmoud Al-Qudsi
b27217e106 terminal_give_to_job() was bypassing the cont branch
If tcgetpgrp for STDIN was already a match, the `cont` branch was
skipped. This wais making the history.expect test fail.
2017-08-06 14:40:18 -07:00
Mahmoud Al-Qudsi
f7b051905e Split child_set_group from setup_child_process
setup_child_process blocks in the case of IO_FILE, meaning it can't
be called before child processes SIGSTOP.
2017-08-06 14:40:18 -07:00
Mahmoud Al-Qudsi
8537cc982e Fixed no-op loop hang 2017-08-06 14:40:18 -07:00
Mahmoud Al-Qudsi
d6c4e66484 Retry setpgid in setup_child_process on EPERM 2017-08-06 14:40:18 -07:00
Mahmoud Al-Qudsi
1ae0272c4e Improved comments 2017-08-06 14:40:18 -07:00
Mahmoud Al-Qudsi
30aa8b3663 No need to unblock last process since it will no longer be SIGSTOP'd 2017-08-06 14:40:18 -07:00
Mahmoud Al-Qudsi
abf6874a2d Be more judicious about when SIGSTOP is performed 2017-08-06 14:40:18 -07:00
Mahmoud Al-Qudsi
99c6f65fee Better set_child_group logic for multi-process jobs 2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
8f2ef082be Clarified job_continue logging 2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
bdcd451030 Handling EPERM in terminal_give_to_job() 2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
8b8a21dcad Fixed exec failure regression
The process_t pointer sent to setup_child_process can actually be 0
without it being failure, as that is what fish sends when `exec` is run
(in the case of INTERNAL_EXEC).

This was causing exec to fail.
2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
9f2addcf27 Set child process group in case of posix_spawn 2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
25afc9b377 Changed how process groups are assigned to child processes
There is no more race condition between parent and child with
regards to setting the process groups. Each child sets it for themselves
and then blocks indefinitely until the parent does what it needs to for
them (having waited for them to set their process groups). They are not
SIGCONT'd until the next process in the chain (if any) starts so that
that process can join their process group and open the pipes.
2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
c81cf56c0b Don't indiscriminately unblock previous cmd for internal builtin/functions
In the last commit, we introduced an indiscriminate if !EXTERNAL check
that unblocks a previously SIGSTOP'd command (if any) to allow the main
loop in exec_job to read from it without deadlocking (since builtins and
functions read directly from input as an optimization, sometimes).

Now only unblocking where a fork will not happen to ensure that if a
builtin ends up forking, that fork'd process is guaranteed to be able to
join the previous process' process group and access its output pipes.
2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
0e9177b590 Don't attempt to unconditionally tcsetpgrp
Setting the process group in a fork/exec scenario is a well-documented
race condition in pretty much any job control mechanism [0] [1]. The
Wikipedia article contradicts the glibc article and suggests that the
best approach is for the parent to wait for the child to become the
process group leader, while the glibc article suggests that both should
make it so (which is what fish did previously). However, I'm running
into cases where tcsetpgrp is causing an EPERM error, which it isn't
documented to do except if the session id for the calling process
differs from that of the target process group (which is never the case
in fish since they are all part of the same session), which should cause
a _different_ error (SIGTTOU to be sent to all members of the calling
process' group).

In all cases, this is easily remedied by checking if the process group
in question is already in control of the terimnal. There's still the
off-chance that in the time between we check that and the time that the
command completes that situation may have changed, but the parent
process is supposed to ignore the result of this call if it errors out.

[0]: https://en.wikipedia.org/wiki/Process_group
[1]: https://www.gnu.org/software/libc/manual/html_node/Launching-Jobs.html
2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
87394a9e0b Fixed race condition in new job control synchronization
We were having child processes SIGSTOP themselves immediately after
setting their process group and before launching their intended targets,
but they were not necessarily stopped by the time the next command was
being executed (so the opposite of the original race condition where
they might have finished executing by the time the next command came
around), and as a result when we sent them SIGCONT, that could never
reach. Now using waitpid to synchronize the SIGSTOP/SIGCONT between the
two.

If we had a good, unnamed inter-process event/semaphore, we could use
that to have a child process conditionally stop itself if the next
command in the job chain hadn't yet been started / setup, but this is
probably a lot more straightforward and less-confusing, which isn't a
bad thing.

Additionally, there was a bug caused by the fact that the main exec_job
loop actually blocks to read from previous commands in the job if the
current command is a built-in that doesn't need to fork.

With this waitpid code, I was able to finally add the SIGSTOP code to
all the fork'd processes in the main exec_job loop without introducing
deadlocks; it turns out that they should be treated just like the main
EXTERNAL fork, but they tend to execute faster causing the same deadlock
described above to occur more readily.

The only thing I'm not sure about is whether we should execute
unblock_pid undconditionally for all !EXTERNAL commands. It makes more
sense to *only* do that if a blocking read were about to be done in the
main loop, otherwise the original race condition could still appear
(though it is probably mitigated by whatever duration the SIGSTOP lasted
for, even if it is SIGCONT'd before the next command tries to join the
process group).
2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
dfac81803b Improved blocked prcoess comments, clarified job vs command chain 2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
f653fbfaf4 fixup! Using SIGSTOP/SIGCONT instead of mmap & sem_t to synchronize jobs 2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
fb13b370e2 Fixed cases where first command in chain would stay blocked
I hadn't realized that the for loop is called multiple times for a given
"single input" (anything that doesn't include semicolons, etc) to fish,
and so processes were being blocked but blocked_pid was lost by the time
that the next job (which was reading from the last process in the
previous job) came around.

Now using a static variable to store the last blocked PID. AFAICT, this
main job control loop is always executed from the same process and
thread, so this shouldn't need to be wrapped in atomics/mutexes, etc.
2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
cafd856831 Using SIGSTOP/SIGCONT instead of mmap & sem_t to synchronize jobs
This code should be more portable, and certainly cleaner. We are
currently always sending SIGCONT to the last process (if it was part of
a job chain) regardless of whether it called SIGSTOP on itself or not,
which should be fine.

Need to explore whether or not the other forks in src/exec.cpp need to
be SIGSTOP'd on run or only the one that we included in this patch.
2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
47d8a7e882 Explicitly nulling chained_wait_prev after munmap() 2017-08-06 14:40:17 -07:00
Mahmoud Al-Qudsi
cdb72b7024 Fixes a race condition in output redirection in job chain
I'm not sure if this happens on all platforms, but under WSL with the
existing codebase, processes in the job chain that pipe their
stdout/stderr to the next process in the job could terminate before the
next job started (on fast enough machines for quick enough jobs).

This caused issues like #4235 and possibly #3952, at least for external
commands. What was happening is that the first process was finishing
before the second process was fully set up. fish would then try to
assign (in both the child and the parent) the process group id belonging
to the process group leader to the new process; and if the first process
had already terminated, it would have ended its process group with it as
well before that happened.

I'm not sure if there was already a mechanism in place for ensuring that
a process remains running at least as long as it takes for the next
process in the chain to join its group, etc., but if that code was
there, it wasn't working in my test setup (WSL).

This patch definitely needs some review; I'm not sure how I should
handle non-external commands (and external commands executed via
posix_spawn). I don't know if they are affected by the race condition in
the first place, but when I tried to add the same "wait for next command
in chain to run before unblocking" that would cause black screens
requiring ctrl+c to bypass.

The "unblock previous command" code was originally run by the next child
to be forked, but was then moved to the shell code instead, making it
more-centrally located and less error-prone.

Note that additional headers may be required for the mmap system call on
other platforms.
2017-08-06 14:40:17 -07:00
Kurtis Rader
35ee28ff24 Revert "finish cleanup of signal blocking code"
This reverts commit fb08fe5f47.

Needed to cleanly apply PR#4268. Will reapply after applying that
change.
2017-08-06 14:38:25 -07:00
Kurtis Rader
4fe9d79438 make tokenize_variable_array() private
Another step towards implementing issue #4200 is to make the
`tokenize_variable_array()` function private to the env.cpp module.
2017-08-06 13:24:34 -07:00
Kurtis Rader
c36ad27618 stop subclassing env_var_t from wcstring
This is the first step to implementing issue #4200 is to stop subclassing
env_var_t from wcstring. Not too surprisingly doing this identified
several places that were incorrectly treating env_var_t and wcstring as
interchangeable types. I'm not talking about those places that passed
an env_var_t instance to a function that takes a wcstring. I'm talking
about doing things like assigning the former to the latter type, relying
on the implicit conversion, and thus losing information.

We also rename `env_get_string()` to `env_get()` for symmetry with
`env_set()` and to make it clear the function does not return a string.
2017-08-06 13:24:34 -07:00
Sam Yu
d87c0424d8 Speedup git prompt
Fix __fish_git_prompt too slow under repo with lots of untracked
files when __fish_git_prompt_showuntrackedfiles enabled.
2017-08-06 13:24:33 -07:00
Sam Yu
0988f53c70 Speedup git prompt
Fix __fish_git_prompt too slow under repo with lots of untracked
files when __fish_git_prompt_showuntrackedfiles enabled.
2017-08-04 20:43:07 -07:00
Kurtis Rader
74fd29fc5c Merge branch 'master' into major 2017-08-04 18:04:07 -07:00
Kurtis Rader
8b79f4e5c9 use the new set -a and set -p in our scripts 2017-08-04 18:02:24 -07:00
Kurtis Rader
67de733b9b implement set --append and set --prepend
Fixes #1326
2017-08-04 17:23:24 -07:00
Kurtis Rader
ff09b8c1ee fix set --show output
I realized I was printing individual var entries using zero based
indexing (like C++) when the indexes should be one based.
2017-08-04 17:13:43 -07:00
Kurtis Rader
cddc4cfb1d fix set --show output
I realized I was printing individual var entries using zero based
indexing (like C++) when the indexes should be one based.
2017-08-04 17:08:25 -07:00
Kurtis Rader
9028104536 use new logmsg and set --show in tests 2017-08-04 13:36:51 -07:00
Kurtis Rader
2f1e70dc1b use new logmsg and set --show in tests 2017-08-04 13:33:47 -07:00
Kurtis Rader
a4ed0837a1 use new logmsg and set --show in tests 2017-08-04 12:51:48 -07:00
Kurtis Rader
e198ed0b2d use new logmsg and set --show in tests 2017-08-04 12:04:32 -07:00
Kurtis Rader
abc9be0250 use new logmsg and set --show in tests 2017-08-04 12:01:16 -07:00
Kurtis Rader
82ff27387a use new logmsg and set --show in tests 2017-08-04 11:41:51 -07:00
Kurtis Rader
03fa5dad12 use new logmsg and set --show in tests 2017-08-04 11:39:43 -07:00
Kurtis Rader
0c69e99d8b use new logmsg and set --show in tests 2017-08-04 11:02:11 -07:00
Kurtis Rader
d4fb75e9f3 fix bug in abbr function
I introduced a bug in the `abbr` function with commit 17dff8c by
referencing the undefined `$cmd` variable. This fixes that.
2017-08-03 23:52:07 -07:00
Kurtis Rader
26472593aa remove the now unused show test util function 2017-08-03 22:02:06 -07:00
Kurtis Rader
10fae1836e use new logmsg and set --show in tests 2017-08-03 22:02:06 -07:00
Kurtis Rader
864dbaeb43 use new logmsg and set --show in tests 2017-08-03 21:37:02 -07:00
Kurtis Rader
7619e62b70 use new logmsg and set --show in tests
Also modify `logmsg` to output additional separator lines to make the
demarcation between tests even clearer.
2017-08-03 21:25:20 -07:00
Kurtis Rader
ecf06f2eb4 use new logmsg and set --show in tests 2017-08-03 20:56:14 -07:00
Kurtis Rader
ec884f4bfd Merge branch 'master' into major 2017-08-03 19:02:33 -07:00
Kurtis Rader
2b2057a56b update changelog re set --show 2017-08-03 19:01:12 -07:00