summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--job.c277
1 files changed, 50 insertions, 227 deletions
diff --git a/job.c b/job.c
index eb5fab6..b85fbb8 100644
--- a/job.c
+++ b/job.c
@@ -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);
}