package Hook::Fork;
use strict;
use warnings;
use parent qw/DynaLoader/;
our $VERSION = '0.01_01';
__PACKAGE__->bootstrap($VERSION);
use Hook::Fork::Task;
use Guard;
use Scalar::Util qw(refaddr);
my %state;
BEGIN {
%state = (
parent => {
head => undef,
tail => undef,
},
child => {
head => undef,
tail => undef,
},
before => {
head => undef,
tail => undef,
},
);
}
sub make_registerer {
my ($which) = @_;
my $state = $state{$which} || die 'wtf';
return sub(&) {
my $code = shift;
my $obj = Hook::Fork::Task->new($code);
$state->{head} = $obj if !$state->{head};
$state->{tail}->append($obj) if $state->{tail};
$state->{tail} = $obj;
if(defined wantarray){
return guard {
if(refaddr $obj == refaddr $state->{tail}){
$state->{tail} = $obj->{prev};
}
if(refaddr $obj == refaddr $state->{head}){
$state->{head} = $obj->{next};
}
$obj->remove;
};
}
}
}
sub make_runner {
my ($which) = @_;
my $state = $state{$which} || die 'wtf';
return sub {
my $node = $state->{head};
return if !$node;
do {
$node->run
} while ($node = $node->{next});
return;
}
}
BEGIN {
*register_parent_fork_hook = make_registerer('parent');
*register_child_fork_hook = make_registerer('child');
*register_before_fork_hook = make_registerer('before');
*run_parent_hooks = make_runner('parent');
*run_child_hooks = make_runner('child');
*run_before_hooks = make_runner('before');
}
sub init {
my $code = _init();
if( $code != 0) {
$! = $code;
die "Problem hooking fork with pthread_atfork: $!";
}
}
use Sub::Exporter -setup => {
exports => [ map { "register_${_}_fork_hook" } keys %state ],
};
sub _get_state {
return \%state;
}
init(); # setup the main handler
1;
__END__
=head1 NAME
Hook::Fork - automatically run code after a fork
=head1 SYNOPSIS
use Hook::Fork qw(register_child_fork_hook);
register_child_fork_hook {
print "fork\n";
};
fork();
# prints "fork" from the child
=head1 DESCRIPTION
Forking can often confuse modules; if a parent opens a socket and sets
come code to run at DESTROY to close it, that DESTROY will run in both
the parent in the child. This means the child exiting can mess up the
parent, or the parent exiting can mess up the child.
This module lets you run some code at fork time, so you can setup a
new socket for the child, or something similar.
=head1 FUNCTIONS
=head2 register_child_fork_hook(&)
This registers another coderef to run in the child after fork.
=head2 register_parent_fork_hook(&)
This registers another coderef to run in the parent after fork.
=head2 register_before_fork_hook(&)
This registers another coderef to run before all forks.
=head1 DETAILS
All three functions work in the same way.
Normally, it pushes another handler onto the list of hooks
If you call it in scalar context, a guard object will be returned that
removes the hook when the guard object goes out of scope.
Otherwise, the hook lives forever.
You can register as many hooks as you like. They run in FIFO order.
=head1 REPOSITORY
L<http://github.com/jrockway/hook-fork>
To contribute, just click "fork", commit changes with impunity, and
then send me a pull request. Thanks in advance!
=head1 AUTHOR
Jonathan Rockway C<< <jrockway@cpan.org> >>
=head1 COPYRIGHT
Copyright (c) 2010 Jonathan Rockway
This module is free software. You can redistribute it under the same
terms as perl itself.