Neglect jobs in subshell with no-fork optimization (#38823)
When the no-fork optimization takes effect on a subshell, it was not
forgetting the original shell's jobs. The subshell was able to falsely
await the original shell's jobs.
The subshell should behave as if it is a child process created by the
fork_and_reset function. This commit extracts part of it as the
become_child function so that it can be directly called in the main
shell process performing the no-fork optimization.
@@ -11,6 +11,8 @@ | ||
11 | 11 | Yash 2.49 |
12 | 12 | |
13 | 13 | * The "\e" escape sequence was not working in the "echo" built-in. |
14 | + * When a last command is a subshell, the parent shell's jobs were | |
15 | + not being cleared when entering the subshell. | |
14 | 16 | |
15 | 17 | ---------------------------------------------------------------------- |
16 | 18 | Yash 2.48 |
@@ -159,6 +159,7 @@ | ||
159 | 159 | __attribute__((nonnull)); |
160 | 160 | static inline void connect_pipes(pipeinfo_T *pi) |
161 | 161 | __attribute__((nonnull)); |
162 | +static void become_child(bool leave); | |
162 | 163 | static void search_command( |
163 | 164 | const char *restrict name, const wchar_t *restrict wname, |
164 | 165 | commandinfo_T *restrict ci, enum srchcmdtype_T type) |
@@ -799,7 +800,12 @@ | ||
799 | 800 | /* fork first if `type' is E_ASYNC, the command type is subshell, |
800 | 801 | * or there is a pipe. */ |
801 | 802 | finally_exit = (type == E_SELF); |
802 | - if (!finally_exit) { | |
803 | + if (finally_exit) { | |
804 | + if (c->c_type == CT_SUBSHELL) | |
805 | + /* No command follows this subshell command, so we can execute the | |
806 | + * subshell directly in this process. */ | |
807 | + become_child(false); | |
808 | + } else { | |
803 | 809 | if (type == E_ASYNC || c->c_type == CT_SUBSHELL |
804 | 810 | || pi->pi_fromprevfd >= 0 || pi->pi_tonextfds[PIPE_OUT] >= 0) { |
805 | 811 | sigtype_T sigtype = (type == E_ASYNC) ? t_quitint : 0; |
@@ -1026,23 +1032,30 @@ | ||
1026 | 1032 | if (sigtype & t_tstp) |
1027 | 1033 | if (save_doing_job_control_now) |
1028 | 1034 | ignore_sigtstp(); |
1029 | - if (sigtype & t_leave) { | |
1030 | - clear_exit_trap(); | |
1031 | - } else { | |
1032 | - phantomize_traps(); | |
1033 | - neglect_all_jobs(); | |
1035 | + become_child(sigtype & t_leave); /* signal mask is restored here */ | |
1036 | + } | |
1037 | + return cpid; | |
1038 | +} | |
1039 | + | |
1040 | +/* Resets traps, signal handlers, etc. for the current process to become a | |
1041 | + * subshell. See `fork_and_reset' for the meaning of the `leave' argument. */ | |
1042 | +void become_child(bool leave) | |
1043 | +{ | |
1044 | + if (leave) { | |
1045 | + clear_exit_trap(); | |
1046 | + } else { | |
1047 | + phantomize_traps(); | |
1048 | + neglect_all_jobs(); | |
1034 | 1049 | #if YASH_ENABLE_HISTORY |
1035 | - close_history_file(); | |
1050 | + close_history_file(); | |
1036 | 1051 | #endif |
1037 | - reset_execstate(true); | |
1038 | - } | |
1039 | - restore_signals(sigtype & t_leave); /* signal mask is restored here */ | |
1040 | - clear_shellfds(sigtype & t_leave); | |
1041 | - is_interactive_now = false; | |
1042 | - suppresserrreturn = false; | |
1043 | - exitstatus = -1; | |
1052 | + reset_execstate(true); | |
1044 | 1053 | } |
1045 | - return cpid; | |
1054 | + restore_signals(leave); /* signal mask is restored here */ | |
1055 | + clear_shellfds(leave); | |
1056 | + is_interactive_now = false; | |
1057 | + suppresserrreturn = false; | |
1058 | + exitstatus = -1; | |
1046 | 1059 | } |
1047 | 1060 | |
1048 | 1061 | /* Searches for a command. |