summaryrefslogtreecommitdiff
path: root/lib/IO/Async/PID.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/IO/Async/PID.pm')
-rw-r--r--lib/IO/Async/PID.pm196
1 files changed, 196 insertions, 0 deletions
diff --git a/lib/IO/Async/PID.pm b/lib/IO/Async/PID.pm
new file mode 100644
index 0000000..fc59f9c
--- /dev/null
+++ b/lib/IO/Async/PID.pm
@@ -0,0 +1,196 @@
+# You may distribute under the terms of either the GNU General Public License
+# or the Artistic License (the same terms as Perl itself)
+#
+# (C) Paul Evans, 2010-2011 -- leonerd@leonerd.org.uk
+
+package IO::Async::PID;
+
+use strict;
+use warnings;
+use base qw( IO::Async::Notifier );
+
+our $VERSION = '0.67';
+
+use Carp;
+
+=head1 NAME
+
+C<IO::Async::PID> - event callback on exit of a child process
+
+=head1 SYNOPSIS
+
+ use IO::Async::PID;
+ use POSIX qw( WEXITSTATUS );
+
+ use IO::Async::Loop;
+ my $loop = IO::Async::Loop->new;
+
+ my $kid = $loop->fork(
+ code => sub {
+ print "Child sleeping..\n";
+ sleep 10;
+ print "Child exiting\n";
+ return 20;
+ },
+ );
+
+ print "Child process $kid started\n";
+
+ my $pid = IO::Async::PID->new(
+ pid => $kid,
+
+ on_exit => sub {
+ my ( $self, $exitcode ) = @_;
+ printf "Child process %d exited with status %d\n",
+ $self->pid, WEXITSTATUS($exitcode);
+ },
+ );
+
+ $loop->add( $pid );
+
+ $loop->run;
+
+=head1 DESCRIPTION
+
+This subclass of L<IO::Async::Notifier> invokes its callback when a process
+exits.
+
+For most use cases, a L<IO::Async::Process> object provides more control of
+setting up the process, connecting filehandles to it, sending data to and
+receiving data from it.
+
+=cut
+
+=head1 EVENTS
+
+The following events are invoked, either using subclass methods or CODE
+references in parameters:
+
+=head2 on_exit $exitcode
+
+Invoked when the watched process exits.
+
+=cut
+
+=head1 PARAMETERS
+
+The following named parameters may be passed to C<new> or C<configure>:
+
+=head2 pid => INT
+
+The process ID to watch. Must be given before the object has been added to the
+containing C<IO::Async::Loop> object.
+
+=head2 on_exit => CODE
+
+CODE reference for the C<on_exit> event.
+
+Once the C<on_exit> continuation has been invoked, the C<IO::Async::PID>
+object is removed from the containing C<IO::Async::Loop> object.
+
+=cut
+
+sub configure
+{
+ my $self = shift;
+ my %params = @_;
+
+ if( exists $params{pid} ) {
+ $self->loop and croak "Cannot configure 'pid' after adding to Loop";
+ $self->{pid} = delete $params{pid};
+ }
+
+ if( exists $params{on_exit} ) {
+ $self->{on_exit} = delete $params{on_exit};
+
+ undef $self->{cb};
+
+ if( my $loop = $self->loop ) {
+ $self->_remove_from_loop( $loop );
+ $self->_add_to_loop( $loop );
+ }
+ }
+
+ $self->SUPER::configure( %params );
+}
+
+sub _add_to_loop
+{
+ my $self = shift;
+ my ( $loop ) = @_;
+
+ $self->pid or croak "Require a 'pid' in $self";
+
+ $self->SUPER::_add_to_loop( @_ );
+
+ # on_exit continuation gets passed PID value; need to replace that with
+ # $self
+ $self->{cb} ||= $self->_replace_weakself( sub {
+ my $self = shift or return;
+ my ( $exitcode ) = @_;
+
+ $self->invoke_event( on_exit => $exitcode );
+
+ # Since this is a oneshot, we'll have to remove it from the loop or
+ # parent Notifier
+ $self->remove_from_parent;
+ } );
+
+ $loop->watch_child( $self->pid, $self->{cb} );
+}
+
+sub _remove_from_loop
+{
+ my $self = shift;
+ my ( $loop ) = @_;
+
+ $loop->unwatch_child( $self->pid );
+}
+
+sub notifier_name
+{
+ my $self = shift;
+ if( length( my $name = $self->SUPER::notifier_name ) ) {
+ return $name;
+ }
+
+ return $self->{pid};
+}
+
+=head1 METHODS
+
+=cut
+
+=head2 $process_id = $pid->pid
+
+Returns the underlying process ID
+
+=cut
+
+sub pid
+{
+ my $self = shift;
+ return $self->{pid};
+}
+
+=head2 $pid->kill( $signal )
+
+Sends a signal to the process
+
+=cut
+
+sub kill
+{
+ my $self = shift;
+ my ( $signal ) = @_;
+
+ kill $signal, $self->pid or croak "Cannot kill() - $!";
+}
+
+=head1 AUTHOR
+
+Paul Evans <leonerd@leonerd.org.uk>
+
+=cut
+
+0x55AA;