summaryrefslogtreecommitdiff
path: root/dmake/unix/runargv.c
diff options
context:
space:
mode:
authorJens-Heiner Rechtien <hr@openoffice.org>2006-04-20 11:19:12 +0000
committerJens-Heiner Rechtien <hr@openoffice.org>2006-04-20 11:19:12 +0000
commit6c45a7078075843f2495b0e27275589ceb07e53d (patch)
treea15b46b67607a756cbd9c4982baddca6529ce559 /dmake/unix/runargv.c
parent9fbdfb64822b9365e8e0336c271ff7b27cc84fc0 (diff)
INTEGRATION: CWS dmake43p01 (1.8.6); FILE MERGED
2006/03/15 14:43:28 vq 1.8.6.11: #i61940# Add and correct some comments. 2006/02/21 23:06:16 vq 1.8.6.10: #i61940# Remove the #undef that unconditionally disabled the use of spawn. 2006/02/12 00:47:37 vq 1.8.6.9: #i61940# Enable the alternative use of spawn instead of fork/exec for dmake. Add the configure option --enable-spawn. Regenerate autotool files. 2006/02/01 23:29:02 vq 1.8.6.8: #i60948# Add -m option family to generate timing information for targets and/or recipes. (Autotools files were regenerated.) 2005/10/11 17:39:40 vq 1.8.6.7: #i54938# Fix problem when building infered .INCLUDE makefiles and doing parallel builds. 2005/09/19 23:48:15 vq 1.8.6.6: #i53148# Move include directive to get some macros defined before using them. 2005/09/17 23:25:21 vq 1.8.6.5: #i53148# Make sure that the command started from _exec_shell really finished before returning. 2005/09/07 01:39:50 vq 1.8.6.4: #i53148# Make sure _attach_cmd can not be used for _exec_shell calls. 2005/09/05 20:56:46 vq 1.8.6.3: #i53148# Move redirection of stdout from parent to child and avoid capturing spurious output from other process queues. 2005/09/05 17:23:46 vq 1.8.6.2: #i53148# Additional patch to make sure that the shell escapes is executed after all previous recipe lines from the same target have finished. 2005/09/04 19:38:15 vq 1.8.6.1: #i53148# Fix $(shell ...) handling for parallel builds with MAXPROCESS > 1. This certainly includes the -P# switch with # > 1.
Diffstat (limited to 'dmake/unix/runargv.c')
-rw-r--r--dmake/unix/runargv.c161
1 files changed, 137 insertions, 24 deletions
diff --git a/dmake/unix/runargv.c b/dmake/unix/runargv.c
index 637335a142dc..98bd2d78d7b9 100644
--- a/dmake/unix/runargv.c
+++ b/dmake/unix/runargv.c
@@ -1,6 +1,6 @@
/* $RCSfile: runargv.c,v $
--- $Revision: 1.8 $
--- last change: $Author: rt $ $Date: 2004-09-08 16:09:39 $
+-- $Revision: 1.9 $
+-- last change: $Author: hr $ $Date: 2006-04-20 12:19:12 $
--
-- SYNOPSIS
-- Invoke a sub process.
@@ -24,8 +24,52 @@
-- LOG
-- Use cvs log to obtain detailed change logs.
*/
+/*
+This file (runargv.c) provides all the parallel process handling routines
+for dmake on unix like operating systems. The following text briefly
+describes the process flow.
+
+Exec_commands() [make.c] builds the recipes associated to the given target.
+ They are build sequentially in a loop that calls Do_cmnd() for each of them.
+
+Do_cmnd() [sysintf.c] feeds the given command or command group to runargv().
+
+runargv() [unix/runargv] The actual child processes are started in this
+ function, even in non parallel builds (MAXPROCESS==1) child processes are
+ created.
+ If recipes for a target are currently running attach them to the process
+ queue (_procs[i]) of that target and return.
+ If the maximum number of running process queues is reached
+ Wait_for_child(?, -1) is used to wait for one to be available.
+ New child processes are started using:
+ spawn: posix_spawnp (POSIX) or spawnvp (cygwin).
+ fork/execvp: Create a client process with fork and run the command with
+ execvp.
+ The parent calls _add_child() to track the child.
+
+_add_child() [unix/runargv] creates a new process queue and enters the child
+ parameters.
+ If Wait_for_completion (global variable) is set the function calls
+ Wait_for_child to waits for the new process queue to be finished.
+
+Wait_for_child(abort_flg, pid) [unix/runargv] waits for the child processes
+ with pid to finish. All finished processes are handled by calling
+ _finished_child() for each of them.
+ If pid == -1 wait for the next child process to finish.
+ If abort_flg is TRUE no further processes will be added to the process
+ queue.
+ If the global variable Wait_for_completion is set then all finished
+ processes are handled until the process with the given pid is reached.
+
+_finished_child(pid, ?) [unix/runargv] removes the finished child from its
+ process queue. If there are more commands in this queue start the next
+ with runargv().
+*/
#include <signal.h>
+
+#include "extern.h"
+
#ifdef HAVE_WAIT_H
# include <wait.h>
#else
@@ -34,13 +78,14 @@
# endif
#endif
-#include "extern.h"
-
-/* temporarily comment out spawn code as it does not actually work yet */
-#undef HAVE_SPAWN_H
-#if HAVE_SPAWN_H
+#if HAVE_SPAWN_H && ENABLE_SPAWN
# include <spawn.h>
#endif
+
+#if __CYGWIN__ && ENABLE_SPAWN
+# include <process.h>
+#endif
+
#include "sysintf.h"
#if HAVE_ERRNO_H
# include <errno.h>
@@ -113,19 +158,33 @@ int shell;
char *cmd;
{
int pid;
+ int st_pq = 0; /* Current _exec_shell target process queue */
char **argv;
- if( _running(target) /*&& Max_proc != 1*/ ) {
- /* The command will be executed when the previous recipe
- * line completes. */
- _attach_cmd( cmd, group, ignore, target, last, shell );
- return(1);
+#if ENABLE_SPAWN && ( HAVE_SPAWN_H || __CYGWIN__ )
+ int old_stdout;
+#endif
+
+ /* Special handling for the shell function macro is required. If the currend
+ * command is called as part of a shell escape in a recipe make sure that all
+ * previous recipe lines of this target have finished. */
+ if( Is_exec_shell ) {
+ if( (st_pq = _running(Shell_exec_target)) != -1 ) {
+ Wait_for_child(FALSE, _procs[st_pq].pr_pid);
+ }
+ } else {
+ if( _running(target) != -1 /*&& Max_proc != 1*/ ) {
+ /* The command will be executed when the previous recipe
+ * line completes. */
+ _attach_cmd( cmd, group, ignore, target, last, shell );
+ return(1);
+ }
}
/* Any Fatal call can potentially loop by recursion because we
* are called from the Quit routine that Fatal indirectly calls
* since Fatal should not happen I have left this bug in here */
- while( _proc_cnt == Max_proc ) {
+ while( _proc_cnt == Max_proc ) { /* This forces sequential execution for Max_proc == 1. */
if( Wait_for_child(FALSE, -1) == -1 ) {
if( ! in_quit() || errno != ECHILD )
Fatal( "Lost a child %d: %s", errno, strerror( errno ) );
@@ -138,17 +197,35 @@ char *cmd;
argv = Pack_argv( group, shell, cmd );
-#if HAVE_SPAWN_H
- if (posix_spawn (&pid, argv[0], NULL, NULL, argv, (char *)NULL))
- { /* posix_spawn failed */
+#if ENABLE_SPAWN && ( HAVE_SPAWN_H || __CYGWIN__ )
+ /* As no other childs are started while the output is redirected this
+ * is save. */
+ if( Is_exec_shell ) {
+ old_stdout = dup(1);
+ close(1);
+ dup( fileno(stdout_redir) );
+ }
+#if __CYGWIN__
+ pid = spawnvp(_P_NOWAIT, argv[0], (const char**) argv);
+#else /* __CYGWIN__ */
+ if (posix_spawnp (&pid, argv[0], NULL, NULL, argv, (char *)NULL))
+ pid = -1; /* posix_spawn failed */
+#endif /* __CYGWIN__ */
+ if( Is_exec_shell ) {
+ close(1);
+ dup(old_stdout);
+ }
+ if(pid == -1)
+ { /* spawn failed */
Error("%s: %s", argv[0], strerror(errno));
Handle_result(-1, ignore, _abort_flg, target);
return(-1);
} else {
_add_child(pid, target, ignore, last);
}
-#else /* HAVE_SPAWN_H */
+#else /* ENABLE_SPAWN && ... */
+ fflush(stdout);
switch( pid=fork() ){
case -1: /* fork failed */
@@ -157,7 +234,14 @@ char *cmd;
return(-1);
case 0: /* child */
+ /* redirect stdout for _exec_shell */
+ if( Is_exec_shell ) {
+ /* org_out = dup(1); */
+ close(1);
+ dup( fileno(stdout_redir) );
+ }
execvp(argv[0], argv);
+ /* restoring stdout is not needed */
Continue = TRUE; /* survive error message */
Error("%s: %s", argv[0], strerror( errno ));
kill(getpid(), SIGTERM);
@@ -167,27 +251,46 @@ char *cmd;
_add_child(pid, target, ignore, last);
}
-#endif /* HAVE_SPAWN_H */
+#endif /* ENABLE_SPAWN && ... */
return(1);
}
PUBLIC int
-Wait_for_child( abort_flg, pid )
+Wait_for_child( abort_flg, pid )/*
+==================================
+ Wait for the child processes with pid to to finish. All finished processes
+ are handled by calling _finished_child() for each of them.
+ If pid == -1 wait for the next child process to finish.
+ If abort_flg is TRUE no further processes will be added to the process
+ queue.
+ If the global variable Wait_for_completion is set then all finished
+ processes are handled until the process with the given pid is reached. */
int abort_flg;
int pid;
{
int wid;
int status;
int waitchild;
+ int is_exec_shell_status = Is_exec_shell;
+
+ /* It is impossible that processes that were started from _exec_shell
+ * have follow-up commands in its process queue. Unset Is_exec_shell
+ * to prevent piping of child processes that are started from the
+ * _finished_child subroutine and reset to its original value when
+ * leaving this function. */
+ Is_exec_shell = FALSE;
waitchild = (pid == -1)? FALSE : Wait_for_completion;
do {
wid = wait(&status);
- if( wid == -1 )
- return(-1);
+
+ if( wid == -1 ) {
+ Is_exec_shell = is_exec_shell_status;
+ return(-1);
+ }
_abort_flg = abort_flg;
_finished_child(wid, status);
@@ -195,6 +298,7 @@ int pid;
}
while( waitchild && pid != wid );
+ Is_exec_shell = is_exec_shell_status;
return(0);
}
@@ -228,6 +332,10 @@ int last;
TALLOC( _procs, Max_proc, PR );
}
+ if( Measure & M_RECIPE )
+ Do_profile_output( "s", M_RECIPE, target );
+
+ /* If _use_i!=-1 then this function is called by _finished_child() */
if( (i = _use_i) == -1 )
for( i=0; i<Max_proc; i++ )
if( !_procs[i].pr_valid )
@@ -265,7 +373,11 @@ int status;
/* Some children we didn't make esp true if using /bin/sh to execute a
* a pipe and feed the output as a makefile into dmake. */
if( i == Max_proc ) return;
- _procs[i].pr_valid = 0;
+
+ _procs[i].pr_valid = 0; /* Not a running process anymore. */
+ if( Measure & M_RECIPE )
+ Do_profile_output( "e", M_RECIPE, _procs[i].pr_target );
+
_proc_cnt--;
dir = DmStrDup(Get_current_dir());
Set_dir( _procs[i].pr_dir );
@@ -287,6 +399,7 @@ int status;
_procs[i].pr_recipe = rp->prp_next;
_use_i = i;
+ /* Run next recipe line. */
runargv( _procs[i].pr_target, rp->prp_ignore, rp->prp_group,
rp->prp_last, rp->prp_shell, rp->prp_cmd );
_use_i = -1;
@@ -319,14 +432,14 @@ CELLPTR cp;
{
register int i;
- if( !_procs ) return(FALSE);
+ if( !_procs ) return( -1 );
for( i=0; i<Max_proc; i++ )
if( _procs[i].pr_valid &&
_procs[i].pr_target == cp )
break;
- return( i != Max_proc );
+ return( i == Max_proc ? -1 : i );
}