From 6c45a7078075843f2495b0e27275589ceb07e53d Mon Sep 17 00:00:00 2001 From: Jens-Heiner Rechtien Date: Thu, 20 Apr 2006 11:19:12 +0000 Subject: 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. --- dmake/unix/runargv.c | 161 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 137 insertions(+), 24 deletions(-) (limited to 'dmake/unix/runargv.c') 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 + +#include "extern.h" + #ifdef HAVE_WAIT_H # include #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 #endif + +#if __CYGWIN__ && ENABLE_SPAWN +# include +#endif + #include "sysintf.h" #if HAVE_ERRNO_H # include @@ -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; iprp_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