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

My program essentially looks like this:

use strict; use warnings; use Config; # $Config use File::Spec::Functions qw(file_name_is_absolute rel2abs); INIT { if(!file_name_is_absolute $0) { print STDERR "Restarting $0 using absolute file name\n"; exec($Config{perlpath},rel2abs($0),@ARGV) or die "exec failed ($!) +"; } } # Main part replaced by dummy: while(1) { sleep(5); print "running $$\n"; }
The idea is the following: The program is supposed (for reasons which are not important here) to be called on Windows, using an absolute path name. The INIT section checks this, and if this is not the case, recursively starts the program by employing an absolute path. This works so far.

The problem is the behaviour of the (recursively) started program: I get every 5 seconds a "running..." message printed, but in addition, when I hit the ENTER key, I see the prompt of my Windows shell, and I can enter commands to this shell. These are executed in parallel to the while() loop of my program.

I had expected that exec() just replaces the current Perl process by a new one. This doesn't seem to happen, and BrowserUk explained in Re^7: exec() on Windows, that the implementation of exec under Windows is flawed, in that it would act like system: Call the external program and wait until it finishes.

However the behaviour I am experiencing, does not match this either. Instead, I get a CMD.EXE shell plus my Perl program in parallel! Any explanation for this? And, does someone know a better way to "restart myself"?

-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: exec on Windows "halfway forks"
by Yary (Pilgrim) on Jun 14, 2010 at 16:47 UTC
    And, does someone know a better way to "restart myself"?
    I would use $^X instead of $Config{perlpath}, since the perl executable might not be where it was configured for it to be. No need to "use Config" to get $^X.
    However the behaviour I am experiencing, does not match this either. Instead, I get a CMD.EXE shell plus my Perl program in parallel! Any explanation for this?
    If you want really interesting behavior, try this in the dummy code:
    while(1) { sleep(5); my $x=scalar<>;print "running $$:$x\n"; }
    Then you'll see that when that thread comes out of sleep, it grabs stdin. Otherwise, the CMD shell has it.

    My guess is that the parent thread, started with the relative path, starts a new thread with the EXEC and then exits, giving control back to the shell. The child thread (started with an absolute path) inherited stdin & stdout, so its input & output are interleaved with the command window.

    My suggestion is to not respawn. Just overwrite $0!

    $0=rel2abs($0) unless file_name_is_absolute $0;
    That is, "lie" to make it look like your script was started with an absolute path.

      My guess is that the parent thread, started with the relative path, starts a new thread with the EXEC and then exits,

      (New process, not new thread)

      But what's causing it to exit? An exec emulation should wait for the child to finish. And it does last I checked. (My Windows system is down, at the moment.)

        You're right, even though this is windows we're talking here, exec really is starting a new process.
      My suggestion is to not respawn. Just overwrite $0! $0=rel2abs($0) unless file_name_is_absolute $0; That is, "lie" to make it look like your script was started with an absolute path.
      This would not do here. The point is that I want to see the process using the absolute path in the Windows Process table (for example, when looking at it using, say, Process Explorer http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx.

      -- 
      Ronald Fischer <ynnor@mm.st>
Re: exec on Windows "halfway forks"
by BrowserUk (Patriarch) on Jun 14, 2010 at 17:54 UTC

    Have you tried checking the return code from exec?

    Because on my system exec never returns, so quite how you are reaching the while loop I do not understand.

    Try running this:

    perl -e"warn $$; exec 'notepad'; warn 'here'"

    You'll see the pid printed & notepad start, but never see 'here'.


    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.
      Because on my system exec never returns, so quite how you are reaching the while loop I do not understand.
      The exec calls the same program. The while loop is reached in the execed program.

        So it does. What a convoluted mechanism to achieve ?

      As you observe correctly, exec does not return. The while loop is only reached if the if condition inside the INIT is false, i.e. (typically) on the recursive call, when the program is called using an absolute path name.

      -- 
      Ronald Fischer <ynnor@mm.st>

        You seem to be leaping through extraordinary hoops in order to achieve what happens naturally on my system:

        C:\test>p1 [0] Perl> print $0;; C:\Perl64\bin\p1.pl [0] Perl> print $^X;; C:\perl64\bin\perl.exe ^C C:\test>cd \Perl64\bin C:\Perl64\bin>.\p1.pl [0] Perl> print $0;; C:\Perl64\bin\p1.pl [0] Perl> print $^X;; C:\perl64\bin\perl.exe

        And under both the above circumstances, ProcessExplorer shows the fully qualified path of both the perl executable and the script being run.

        Do you know of circumstances where that is not the case?

Re: exec on Windows "halfway forks"
by JavaFan (Canon) on Jun 14, 2010 at 16:53 UTC
    Out of curiousity, do you see the same effect if you take the body of the INIT out of the INIT? That is, remove the "INIT {" line and it matching close brace.

      Yes, of course one would. Because exec(3) isn't implemented on Windows so Perl emulates it by spawning a subprocess and then having the parent process exit. That is the best that can be done without something much more heavy-handed like cygwin.

      So the choices on Windows are: 1) Use system then exit and have an extra process hanging around, 2) Use exec and realize that the calling process won't be able to wait because the middle process will quickly exit, 3) Use cygwin Perl.

      - tye        

      do you see the same effect if you take the body of the INIT out of the INIT?
      Yes.

      -- 
      Ronald Fischer <ynnor@mm.st>