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

Running
BEGIN{fork;print"inner\n"} print "outer\n"
on a windows box will produce the output
inner
inner
outer
due to the way fork emulation works. Is there some nice way to make this work as it should on all platforms?

Replies are listed 'Best First'.
Re: In the BEGINing there were no forks?
by RazorbladeBidet (Friar) on Feb 14, 2005 at 19:59 UTC
    perlfork explains the caveats of fork() with BEGIN blocks.

    <snip>
    BEGIN blocks The fork() emulation will not work entirely correctly when called from within a BEGIN block. The forked copy will run the contents of the BEGIN block, but will not continue parsing the source stream after the BEGIN block. This limitation arises from fundamental technical difficulties in cloning and restarting the stacks used by the Perl parser in the middle of a parse.
    </snip>
      Yes. I know. Hence my question. I could use some stupid method of a system call to call my perl code again, and work out a crazy way to make sure that that only happens once, in the BEGIN block. But that is ugly. Is there a cunning nice way?
        Ahh, I see. My apologies

        Well, for the child, you're pretty much hosed outside the begin block, so maybe split up the BEGIN block into one file and the logic into another?

        BEGIN { $pid = fork(); require "logic.pl" if $pid; } require "logic.pl";


        Dunno, just a thought. Good luck with it
Re: In the BEGINing there were no forks?
by Zaxo (Archbishop) on Feb 14, 2005 at 20:07 UTC

    Windows' fork emulation is just an emulation, and it shows a bug there. With a genuine fork you sould see:

    inner outer inner outer
    or a possible but unlikely reversal of the middle two. Fork produces two processes where there was one, each continuing from the location of fork. Both processes see and do both print statements. Here's a version which skips "outer" for the child.
    BEGIN { my $cpid = fork; print "inner\n"; exit 0 unless $cpid; } print "outer\n";
    It would be more to the point to ask if Windoew' emulation can be fixed to match the real thing.

    Update: Oh, that's what you were asking. </sigh>

    After Compline,
    Zaxo

      I think you got the wrong end of the stick there. The desired behaviour is the correct, UNIX style forking. I don't want to fix UNIX to be like Windows in the code, but the other way around. Is it possible to improve the emulation from within the code itself?
Re: In the BEGINing there were no forks?
by Anonymous Monk on Feb 15, 2005 at 09:42 UTC
    I've no Windows box, so I can't try. But if the problem is forking inside a BEGIN, why don't you just move all the code one step lower in the compile/run order? That is, a BEGIN becomes "normal" code, and "normal" code becomes an eval:
    fork; print "inner\n"; eval qq{print "outer\n"};
Re: In the BEGINing there were no forks?
by Anonymous Monk on Feb 14, 2005 at 20:29 UTC
    Actually, this gets weirder, consider
    BEGIN{if(fork){die}}
    and
    BEGIN{if(!fork){die}}
    On Activestate 5.8.4, the first produces
    Died at fork.pl line 1.
    BEGIN failed--compilation aborted at j2.pl line 1.
    
    whilst the second
    panic: restartop
    Now why should that be?
      The only thing I can think of would be that fork is returning undef because it was unsuccessful

      I've never actually seen that message (panic: restartop) before, though.

      The first error has nothing to do with fork. It happends always and on every os (or atleast on the ones I tested)...

      I guess it comes because the BEGIN-block is run at compile-time, and the compiling of the code failed. (you called die...)

      perl -wle "BEGIN { die; }" produces exactly the same error.

      I guess (I'm not sure of this at all!) that the second error occures because the parent died before the child was run... (! fork() means not undef, and not 0, if the parents executes fork() then it returns the pid of the child which is not 0).

      This would then mean that the proces is dead, but that there are still threads alive, which should give problems (or not?)... (I could be wrong on this one, as I said, I'm guessing)

      The fork emulation on Windows creates a new thread inside the proces, fork on unix/linux/... creates a new proces, which is slightly different I guess.

      Update, as RazorbladeBidet I got it backward.. What was I thinking? :)

        I think you have it backwards (or maybe I do - it's getting late for me :) )

        if (!fork) { die; }


        Should die only when fork is not true, that is 0 or undef.

        Thus, it's either the child (which really doesn't make sense because a dying child is no big deal) or it's undef, which means something bad happened.

        The first one, as you said, is your normal error. I didn't think that one was under question.
Re: In the BEGINing there were no forks?
by FitTrend (Pilgrim) on Feb 14, 2005 at 21:32 UTC

    In my experience forking on windows can lead to some unusual behavior. Based on what I've found from the internet and others have pointed out, fork() doesn't work like we'd expect it to on *NIX.

    To accomplish what I wanted, I've put aside fork and used Win32::Process, threads, and system(). I've looked into POE (which is great). However, some of the modules required (the ones I've needed of course) do not install with ActiveState.

    Hope this helps

      OK, I just can't stand it anymore. I'm sorry in advance but I JUST GOTTA!!!

      In my experience, forking on Windows leads to complaints from the neighbors.

      There, I feel much better now.

      Jack

Re: In the BEGINing there were no forks?
by BrowserUk (Patriarch) on Feb 14, 2005 at 20:13 UTC

    Easy--don't fork it in a begin block.

    P:\test>perl -wle"fork; print 'inner'; print 'outer';" inner outer inner outer

    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.
      Well yes. Or code in INTERCAL, or whatever. The fact that there are other ways to do something does not invalidate the question. Also, it messes up my cunning plan for an obfu.

        Make it a non-win obfu. I'd like to see it:)


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.