Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is "fork inside BEGIN ... a horrible prospect" in Perl?

Tags:

fork

perl

As part of a comment on another SO Q&A, a user noted that:

fork inside BEGIN is a horrible prospect

Why is that a "horrible prospect"? (on a technical level - let's leave aside readability or prettiness of design)

use strict;
BEGIN {
    # Begin block to fork off a child process.
    # This is done in BEGIN, because otherwise My::HeavyModule module
    # will be loaded BEFORE the fork and thus inherited by child process
    # which is something we want to avoid

    my $init = 1; # Some lightweight init code

    if (my $pid = fork()) {
        # Nothing to do here, proceed to the rest of the main program
    } else {
        die "cannot fork" unless defined $pid;
        print "Child process started!\n";
        exit 0;
    }
} # End BEGIN block

use My::HeavyModule; # Very heavyweight on both startup time and memory 

# Start parent process logic using My::HeavyModule;

Just to be clear: I am NOT asking if there are better ways to achieve what this code is doing. I'm asking why this approach was called "horrible", NOT whether it can be replaced by something that may be better.

like image 458
DVK Avatar asked Mar 27 '14 16:03

DVK


1 Answers

I wrote the following comment which you are referring to:

fork inside BEGIN is a horrible prospect. You could also delay compilation for some parts using eval "string" or require, but that also has its issues.

although not because of technical reasons (there are none beyond what dan1111 mentioned, and an explicit portability warning in perlfork), but because it completely broke my expectations about any programs behaviour.

  • Each piece of code has a compile time. I expect that packages and classes will be set up here, and maybe that some metaprogramming takes place.

  • Then, there is a run time during which the main control flow of our program occurs, during which the job of this program is being carried out.

Perl complicates this simple distinction in that one block's compile time is another block's run time (e.g. via BEGIN or use or eval or require). But there is always a clear reference point: The phase of $0, the program originally being invoked (see also ${^GLOBAL_PHASE}). There will always be cases where it is a good idea to do funky things during the main script's compile time, but this doesn't necessitate that doing so would be a best practice – on the contrary, and thus my objection that doing so would be a “horrible prospect”.

If the main job of your program is to kick off two other independent programs, it might look like this:

use strict;
use warnings;

my $pid = fork;
if (not defined $pid) {
    die "welp, can't fork: $!";
}
if ($pid) {
    exec $^X, "heavy_program_you_intended_to_be.pl", @ARGV;
}
else {
    ... # background yourself, etc.
    exec $^X, "light_program_you_intended_to_kick_off.pl";
}

But it would not fork in a BEGIN. I think this is one of many examples where you can do something with Perl, but this doesn't mean you should.

like image 73
amon Avatar answered Sep 23 '22 22:09

amon