diff options
Diffstat (limited to 'job.c')
-rw-r--r-- | job.c | 277 |
1 files changed, 50 insertions, 227 deletions
@@ -176,152 +176,34 @@ child_error (target_name, exit_code, exit_sig, coredump, ignored) } } -extern void block_remote_children (), unblock_remote_children (); +int child_died = 0; -extern int fatal_signal_mask; - -#ifdef USG -/* Set nonzero in the interval when it's possible that we may see a dead - child that's not in the `children' chain. */ -static int unknown_children_possible = 0; -#endif - - -/* Block the child termination signal and fatal signals. */ - -static void -block_signals () -{ -#ifdef USG - - /* Tell child_handler that it might see children that aren't yet - in the `children' chain. */ - unknown_children_possible = 1; - - /* Ignoring SIGCLD makes wait always return -1. - Using the default action does the right thing. */ - (void) SIGNAL (SIGCLD, SIG_DFL); - -#else /* Not USG. */ - - /* Block the signals. */ - (void) sigblock (fatal_signal_mask | sigmask (SIGCHLD)); - -#endif - - block_remote_children (); -} - -/* Unblock the child termination signal and fatal signals. */ -static void -unblock_signals () -{ -#ifdef USG - - (void) SIGNAL (SIGCLD, child_handler); - - /* It should no longer be possible for children not in the chain to die. */ - unknown_children_possible = 0; - -#else /* Not USG. */ - - /* Unblock the signals. */ - (void) sigsetmask (sigblock (0) & ~(fatal_signal_mask | sigmask (SIGCHLD))); - -#endif - - unblock_remote_children (); -} - -static char *signals_blocked_p_stack = 0; -static unsigned int signals_blocked_p_max; -static unsigned int signals_blocked_p_depth; - -/* Make signals blocked in FLAG is nonzero, unblocked if FLAG is zero. - Push this setting on the signals_blocked_p_stack, so it can be - popped off by pop_signals_blocked_p. */ - -void -push_signals_blocked_p (flag) - int flag; +/* Notice that a child died. + reap_children should be called when convenient. */ +int +child_handler (sig) + int sig; { - int blocked; - - if (signals_blocked_p_stack == 0) - { - signals_blocked_p_max = 8; - signals_blocked_p_stack = (char *) xmalloc (8); - signals_blocked_p_depth = 1; - signals_blocked_p_stack[0] = flag; - - blocked = 0; - } - else - { - if (signals_blocked_p_depth == signals_blocked_p_max) - { - signals_blocked_p_max += 8; - signals_blocked_p_stack - = (char *) xrealloc(signals_blocked_p_stack, - signals_blocked_p_max); - } - - blocked = (signals_blocked_p_depth > 0 - && signals_blocked_p_stack[signals_blocked_p_depth - 1]); - - signals_blocked_p_stack[++signals_blocked_p_depth - 1] = flag; - } - - if (blocked && !flag) - unblock_signals (); - else if (flag && !blocked) - block_signals (); + child_died = 1; + return 0; } -/* Pop the signals_blocked_p setting from the stack - and block or unblock signals as appropriate. */ - -void -pop_signals_blocked_p () -{ - int blocked, block; - - blocked = (signals_blocked_p_depth > 0 - && signals_blocked_p_stack[signals_blocked_p_depth-- - 1]); - - block = (signals_blocked_p_depth > 0 - && signals_blocked_p_stack[signals_blocked_p_depth - 1]); - - if (block && !blocked) - block_signals (); - else if (blocked && !block) - unblock_signals (); -} - extern int shell_function_pid, shell_function_completed; -/* Handle a child-termination signal (SIGCHLD, or SIGCLD for USG), - storing the returned status and the new command state (`cs_finished') - in the `file' member of the `struct child' for the dead child, - and removing the child from the chain. - - If we were called as a signal handler, SIG should be SIGCHLD - (SIGCLD for USG). If instead it is zero, we were called explicitly - and should block waiting for running children. - If SIG is < 0, - SIG is the maximum number of children to bury (record - status of and remove from the chain). */ +/* Reap dead children, storing the returned status and the new command + state (`cs_finished') in the `file' member of the `struct child' for the + dead child, and removing the child from the chain. If BLOCK nonzero, + reap at least one child, waiting for it to die if necessary. If ERR is + nonzero, print an error message first. */ -int -child_handler (sig) - int sig; +void +reap_children (block, err) + int block, err; { WAIT_T status; - unsigned int dead_children = 0; - - if (sig > 0) - block_signals (); - while (1) + while ((children != 0 || shell_function_pid != 0) && + (block || child_died)) { int remote = 0; register int pid; @@ -329,6 +211,14 @@ child_handler (sig) register struct child *lastc, *c; int child_failed; + if (err && !child_died) + { + fflush (stdout); + error ("*** Waiting for unfinished jobs...."); + } + + child_died = 0; + /* First, check for remote children. */ pid = remote_status (&exit_code, &exit_sig, &coredump, 0); if (pid < 0) @@ -336,18 +226,11 @@ child_handler (sig) /* No remote children. Check for local children. */ #ifdef WAIT_NOHANG - if (sig > 0) + if (!block) pid = WAIT_NOHANG (&status); else +#endif pid = wait (&status); -#else /* USG and don't HAVE_SYS_WAIT. */ - /* System V cannot do non-blocking waits, so we have two - choices if called as a signal handler: handle only one - child (there may be more if the signal was blocked), - or block waiting for more. The latter option makes - parallelism useless, so we must choose the former. */ - pid = wait (&status); -#endif /* HAVE_SYS_WAIT or not USG. */ if (pid <= 0) /* No local children. */ @@ -372,17 +255,7 @@ child_handler (sig) shell_function_completed = -1; else shell_function_completed = 1; - - /* Check if we have reached our quota of children. */ - ++dead_children; - if (sig < 0 && dead_children == -sig) - break; -#if defined(USG) && !defined(HAVE_SYS_WAIT) - else if (sig > 0) - break; -#endif - else - continue; + break; } child_failed = exit_sig != 0 || exit_code != 0; @@ -396,20 +269,13 @@ child_handler (sig) if (c == 0) { /* An unknown child died. */ -#ifdef USG - if (!unknown_children_possible) - { -#endif - char buf[100]; - sprintf (buf, "Unknown%s job %d", remote ? " remote" : "", pid); - if (child_failed) - child_error (buf, exit_code, exit_sig, coredump, - ignore_errors_flag); - else - error ("%s finished.", buf); -#ifdef USG - } -#endif + char buf[100]; + sprintf (buf, "Unknown%s job %d", remote ? " remote" : "", pid); + if (child_failed) + child_error (buf, exit_code, exit_sig, coredump, + ignore_errors_flag); + else + error ("%s finished.", buf); } else { @@ -436,8 +302,9 @@ child_handler (sig) child_failed = 0; } - /* If there are more commands to run, try to start them. */ - start_job (c); + if (!err) + /* If there are more commands to run, try to start them. */ + start_job (c); switch (c->file->command_state) { @@ -454,14 +321,15 @@ child_handler (sig) break; default: - error ("internal error: `%s' command_state \ -%d in child_handler", c->file->name); + error ("internal error: `%s' has bogus command_state \ +%d in reap_children", + c->file->name, c->file->command_state); abort (); break; } } - /* Set the state flag to say the commands have finished. */ + /* Notice if the target of the commands has been changed. */ notice_finished_file (c->file); /* Remove the child from the chain and free it. */ @@ -477,52 +345,11 @@ child_handler (sig) /* If the job failed, and the -k flag was not given, die. */ if (child_failed && !keep_going_flag) die (1); - - /* See if we have reached our quota for blocking. */ - ++dead_children; - if (sig < 0 && dead_children == -sig) - break; -#if defined(USG) && !defined(HAVE_SYS_WAIT) - else if (sig > 0) - break; -#endif } - } - -#ifdef USG - if (sig > 0) - (void) SIGNAL (sig, child_handler); -#endif - - if (sig > 0) - unblock_signals (); - - return 0; -} - -/* Wait for N children, blocking if necessary. - If N is zero, wait until we run out of children. - If ERR is nonzero and we have any children to wait for, - print a message on stderr. */ - -void -wait_for_children (n, err) - unsigned int n; - int err; -{ - push_signals_blocked_p (1); - - if (err && (children != 0 || shell_function_pid != 0)) - { - fflush (stdout); - error ("*** Waiting for unfinished jobs...."); + /* Only block for one child. */ + block = 0; } - - /* Call child_handler to do the work. */ - (void) child_handler (- (int) n); - - pop_signals_blocked_p (); } /* Free the storage allocated for CHILD. */ @@ -754,13 +581,16 @@ new_job (file) char **lines; register unsigned int i; + /* Reap any children that might have finished recently. */ + reap_children (0, 0); + /* Chop the commands up into lines if they aren't already. */ chop_commands (cmds); if (job_slots != 0) /* Wait for a job slot to be freed up. */ while (job_slots_used == job_slots) - wait_for_children (1, 0); + reap_children (1, 0); /* Expand the command lines and store the results in LINES. */ lines = (char **) xmalloc (cmds->ncommand_lines * sizeof (char *)); @@ -771,8 +601,6 @@ new_job (file) /* Start the command sequence, record it in a new `struct child', and add that to the chain. */ - push_signals_blocked_p (1); - c = (struct child *) xmalloc (sizeof (struct child)); c->file = file; c->command_lines = lines; @@ -801,14 +629,12 @@ new_job (file) break; } - pop_signals_blocked_p (); - if (job_slots == 1 && file->command_state == cs_running) { /* Since there is only one job slot, make things run linearly. Wait for the child to finish, setting the state to `cs_finished'. */ while (file->command_state != cs_finished) - wait_for_children (1, 0); + reap_children (1, 0); } } @@ -834,9 +660,6 @@ child_execute_job (stdin_fd, stdout_fd, argv, envp) (void) close (d); } - /* Don't block signals for the new process. */ - unblock_signals (); - /* Run the command. */ exec_command (argv, envp); } |