A concise forking idiom in pure Perl

One of the first module I took over as a maintainer on CPAN was Proc::Fork.

It is a beautiful interface.

It did get a bit uglier in relatively recent times when I added the run_fork wrapper, an unfortunate necessity in certain cases.

But for small single-file-redistributable programs that can be offered to people who are merely users of a Unix system, who do not have any sort of CPAN setup or installation experience, it always felt like a burden to pull in a dependency for something as… insubstantial as this little bit of syntactic sugar:

run_fork {
    child {
        # ...
    }
    parent {
        my $kid = shift;
        # ...
    }
}

Just the other day, it occurred to me that with given/when, it is easy to construct an idiom for forking that is just as concise, if not self-documenting to the same extent:

given ( fork ) {
    when ( undef ) { die "Couldn't fork: $!\n" }
    when ( 0 ) {
        # ... child code here ...
    }
    default {
        my $kid = $_;
        # ... parent code here ...
    }
}

If the code is to run on older perls as well (unfortunately 5.8 is still quite widespread), a less elegant version can be constructed with foreach:

for ( scalar fork ) {
    die "Couldn't fork: $!\n" if not defined;
    if ( my $kid = $_ ) {
        # ... parent code here ...
    }
    else {
        # ... child code here ...
    }
}

[Update: unfortunately, in all perls released to date, fork in list context returns () on failure so it is necessary to spell this scalar fork, ruining the æsthetic. Sigh.]

While by this point we’ve gone a far cry away from the simple explicitness of the Proc::Fork API, to my mind it is still a lot better than the usual (non-)“idiom” without the outer scope:

my $kid = fork;
die "Couldn't fork: $!\n" if not defined $kid;
if ( $kid ) {
    # ... parent code here ...
}
else {
    # ... child code here ...
}

This leaves $kid declared in the outer scope and leaves the flow control scattered below it in the same scope, with nothing holding together the whole thing visually as one unit – to the point where I get a strong Proc::Fork craving to clean away the chaff…

3 Comments

That's a creative use of "given". Looks reasonable to me. Thanks for sharing it.

For Proc::Fork Why not do localise $_ to the first argument?

For fork calls I find 3 functions helpful:

# imported from elsewhere
sub kid() {
    defined && not int
}
sub parent() {
    defined && int
}
sub pid() {
    int
}    
# main module
for (fork) {
    if (parent) {
        warn "parent ", pid;
        waitpid(pid, 0);
    } elsif (kid) {
        warn "kid ",pid;
    } else {
        die "error!"
    }
}
use constant {
  FORK_ERROR  => undef,
  FORK_CHILD  => 0,
  FORK_PARENT => sub { $_[0] > 0 },
};

for (fork) {
  when (FORK_ERROR) {
    confess "Error forking";
  }
  when (FORK_PARENT) {
    my $child_pid = $_;
    ...;
  }
  when (FORK_CHILD) {
    ...;
  }
}

Leave a comment

About Aristotle

user-pic Waxing philosophical