perlpal has asked for the wisdom of the Perl Monks concerning the following question:

Hi Perl Monks,

My requirement is to initiate multiple perl scripts , each residing in a different directory, as independent processes on a WINDOWS(2008) system, simultaneously. I have attempted to implement this using fork in the Cygwin environment.However, i end up with sequential execution of scripts rather than the simultaneous execution required.

The code is mentioned below for reference :

#!C:\Perl\bin\perl.exe use warnings; use strict; my @drive_dir = ('perl /a/dir_file_create.txt' , 'perl /b/dir_file_cre +ate.txt', 'perl /c/dir_file_create.txt'); # Create a new process my $proc_cmd = fork(); if (!defined $proc_cmd) { die "Unable to fork: $!"; } elsif ($proc_cmd == 0){ # Child foreach my $create (@drive_dir){ exec ("$create"); } die "Unable to exec: $!" if $!; } else { # Parent print "\nThe process id is : $proc_cmd \n"; kill 9,$proc_cmd; wait(); print "Child exited with: ",$? >> 8,"\n"; }

Thanks in advance.

Replies are listed 'Best First'.
Re: Unable to emulate fork functionality on Windows
by Corion (Patriarch) on Mar 29, 2011 at 10:29 UTC

    You have at least one logic error in your program. You will only ever fork once and then exec the first task. The second and third task will never get started. See Parallel::ForkManager or runN for how to conveniently launch multiple programs from fork().

    On plain Windows Perl, system(1, ...) starts a detached process. I don't know whether cygwin Perl also supports that.

Re: Unable to emulate fork functionality on Windows
by BrowserUk (Patriarch) on Mar 29, 2011 at 11:32 UTC

    If you redefine your problem to be "I want to run N commands concurrently", then your entire program could be just:

    use strict; use threads; my @drive_dir = ( 'perl /a/dir_file_create.txt' , 'perl /b/dir_file_create.txt', 'perl /c/dir_file_create.txt' ); my @kids = map async{ system $_; }, @drive_dir; $_->join for @kids;

    Which will work without requiring fork emulation or cygwin.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Unable to emulate fork functionality on Windows
by cavac (Prior) on Mar 29, 2011 at 11:10 UTC

    Something like this should work (this is an abbreviated version of my service control class).

    Warning, this might/will need some fiddling from you. I pulled this out of my (DarkPAN) repo and removed much of the code to make the (for your problem) relevant parts a bit easier to understand.

    First, you need to use some Win32 modules:

    use Win32::Process; use Win32;
    We'll use a hash reference to store the app's config and runtime data (could also be another module...).
    sub start_app { my ($self, $app) = @_; my $ProcessObj; if(!Win32::Process::Create($ProcessObj, $app->{app}, $app->{app}." ".$app->{cmdlineopts}, 0, NORMAL_PRIORITY_CLASS, $self->{basePath})) { print "Error starting app " . $app->{description} . ": " . Win32::FormatMessage( Win32::GetLastError() ) . "\n"; $app->{handle} = undef; return 0; } else { $app->{handle} = $ProcessObj; my $app_pid = $app->{handle}->GetProcessID(); print "Started app " . $app->{description} . " with PID " . $a +pp_pid . "\n"; return 1; } }
    You may also want to restart the external application if it quits, so you do something like this every few seconds:
    sub check_app { my ($self, $app) = @_; # Ignore if this application was never started # or was intentionally stopped if(!defined($app->{handle})) { return $self->start_app($app); } # First, check if the process exited if($app->{handle}->Wait(1)) { # Process exited, so, restart print "Process exit detected: " . $app->{description} . "!n"; return $self->start_app($app); } # All ok, just return true return 1; }
    Ok, you might also want to intentionally kill an external app, either because it hangs or you just don't like its color:
    sub stop_app { my ($self, $app) = @_; if(defined($app->{handle}) && $app->{handle}) { print "Killing app " . $app->{description} . "...\n"; my $app_pid = $app->{handle}->GetProcessID(); $app->{handle}->Kill(0); $app->{handle}->Wait(2000); $app->{handle} = undef; print "...killed.\n"; } else { print "App " . $app->{description} . " already killed\n"; } }
    I hope the code is useable for you.
Re: Unable to emulate fork functionality on Windows (spawn)
by tye (Sage) on Mar 29, 2011 at 17:12 UTC

    You don't need "fork", all you need is "spawn". Just because Unix spells "spawn" as "fork then exec" doesn't mean that is how you need to do it. Unfortunately, Perl doesn't expose a portable implementation of "spawn" despite having implemented one internally.

    But if you have Windows Perl ("native" not cygwin), then your problem is solved as easily as:

    system( 1, $^X, $_ ) for qw< /a/dir_fil­e_create.t­xt /b/dir_fil­e_create.t­xt /c/dir_fil­e_create.t­xt >; while( 0 <= wait() ) { warn "Child exited with ", $?>>8, $/; }

    - tye        

Re: Unable to emulate fork functionality on Windows
by anonymized user 468275 (Curate) on Mar 29, 2011 at 15:55 UTC
    I'd start with a dummy child, make the other children wait for it, and then kill it e.g.
    #!C:\Perl\bin\perl.exe use warnings; use strict; my @drive_dir = ( 'perl /a/dir_file_create.txt', 'perl /b/dir_file_create.txt', 'perl /c/dir_file_create.txt'); my $pid0 = open my $nh, 'type nul |'; # spawn dummy child my @pid; for ( @drive_dir ) { my $pid; $pid = fork and push @pid, $pid; unless( $pid ) { # non-dummy child waitpid $pid0, 0; # wait for dummy child to die system( 'perl $_' ); exit; } } # parent sleep 1; #allow children a little time to start <$nh>; close $nh; # kills dummy child waitpid $_, 0 for ( @pid ); # keep parent alive until all children don +e

    One world, one people