A concise forking idiom in core 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;
# ...
}
}
[Update: Now disregard the following entirely, and instead go read the followup.]
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…
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) { ...; } }