http://qs321.pair.com?node_id=166538

Here's another gotcha that bit me just yesterday, that, in retrospect seems so obvious:

sub main_code { while(1) { eval{ fork_and_do_stuff() }; print STDERR "error, but we go on anyways\n"; } } sub fork_and_do_stuff { my $pid = fork if( $pid == 0 ) { ## simplified... do_stuff(); exit 1; } }

So what happens if you die() within do_stuff() ? The control would actually go to the eval block enclosing the fork_and_do_stuff() call! I've inadvertantly a child process that would execute the parent code.

That obviously sucks.

The solution is to put yet another eval block in the child code:

sub fork_and_do_stuff { my $pid = fork if( $pid == 0 ) { eval{ do_stuff() }; exit 1; } }

After this, I'm definitely going to be in the habit of putting all forked code in some sort of eval block to make sure that I can safely wrap the forking code in another eval block...

Replies are listed 'Best First'.
Re: Eval/Fork lesson of the day
by graff (Chancellor) on May 15, 2002 at 03:45 UTC
    ... I'm definitely going to be in the habit of putting all forked code in some sort of eval block to make sure that I can safely wrap the forking code in another eval block...

    ... or maybe you'd look for other ways to get the job done without having to use "eval { ...fork... }" in the first place. I'm no jedi when it comes to fork, but I know it well enough to respect it, and this is the first time I've seen anyone put a fork call inside an eval block. Just curious... what situation could make this a preferred approach?

      The basic premise was that I needed to catch all errors from a series of steps, and if there were any errors in the middle, I needed to do whatever to undo the changes:

      while(1) { eval { do_this(); do_that(); fork_and_stuff(); and_why_not_do_this_as_well(); }; if( $@ ) { undo_changes(); log( "there was, like, something wrong: $@" ); } }

      It just so happened that one of the subs actually forked, and hence the problem. I wasn't trying to eval the fork() per se... ;)

        That makes sense, but it does look a bit tricky. So, I assume you would really want to hit the "undo" block if the fork fails; and maybe you would want to make sure the fork call is the last step in the eval block, so that if any of the other steps fail, you could save yourself the trouble of forking before undoing things.

        When the fork "succeeds", then any problems encountered by the child will be asynchronous with respect to this process, right? And that might make it hard to "undo" whatever the child does, I think. (I'm sure there's a way to deal with that, but as I said, fork is not one of my strong suits.)

Re: Eval/Fork lesson of the day
by theguvnor (Chaplain) on May 14, 2002 at 22:02 UTC

    Don't forget your semi-colon (';') after forking: my $pid = fork;

    ..Jon

      Thanks for this article, I found it quite useful. I ran into this when writing a test for a package I wrote to ensure that it properly bombed with tainted data. The package wrapped an Unix command, and forked.

      I was quite confused when Test::Harness started showing duplicate results for the same test number (one passing and one failing:). Quite a bit of head banging until I could reduce the issue to something which lead me to google this article.

      So I think this issue can crop up in some unexpected places.

        So there is some side effect / memory sharing magic going on when the fork is inside an eval, clearly.

        Things work well when I'm running a loop over some cases and just fork (and have the forked children self monitor and die with error or die with success).

        When I had my forked child do the same thing but its within an eval, then I got the same sequence of loop iterations, but also got a lot of reinterpretition of cases the loop had already evaluated.

        What is it about being inside an eval that causes this fork oddity?

        The use of eval is handy to capture the die results in the parent and then log them.