Acme-Ghost

 view release on metacpan or  search on metacpan

LICENSE  view on Meta::CPAN

meet all of these conditions:

a) You must cause the modified files to carry prominent notices stating that you
changed the files and the date of any change.

b) You must cause any work that you distribute or publish, that in whole or in
part contains or is derived from the Program or any part thereof, to be licensed
as a whole at no charge to all third parties under the terms of this License.

c) If the modified program normally reads commands interactively when run, you
must cause it, when started running for such interactive use in the most ordinary
way, to print or display an announcement including an appropriate copyright
notice and a notice that there is no warranty (or else, saying that you provide a
warranty) and that users may redistribute the program under these conditions,
and telling the user how to view a copy of this License. (Exception: if the
Program itself is interactive but does not normally print such an announcement,
your work based on the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole. If identifiable
sections of that work are not derived from the Program, and can be reasonably
considered independent and separate works in themselves, then this License,

eg/ghost_acme.pl  view on Meta::CPAN

#!/usr/bin/perl -w
use strict;

my $g = MyGhost->new(
    logfile => 'daemon.log',
    pidfile => 'daemon.pid',
);

exit $g->ctrl(shift(@ARGV) // 'start'); # start, stop, restart, reload, status

1;

package MyGhost;

use parent 'Acme::Ghost';

sub init {
    my $self = shift;
    $SIG{HUP} = sub { $self->hangup }; # Listen USR2 (reload)
}
sub hangup {
    my $self = shift;
    $self->log->debug("Hang up!");
}
sub startup {
    my $self = shift;
    my $max = 100;
    my $i = 0;
    while ($self->ok) {
        $i++;
        sleep 3;
        $self->log->debug(sprintf("> %d/%d", $i, $max));
        last if $i >= $max;
    }
}

eg/ghost_ae.pl  view on Meta::CPAN

#!/usr/bin/perl -w
use strict;

my $g = MyGhost->new(
    logfile => 'daemon.log',
    pidfile => 'daemon.pid',
);

exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, reload, status

1;

package MyGhost;

use parent 'Acme::Ghost';
use AnyEvent;

sub startup {
    my $self = shift;
    my $quit = AnyEvent->condvar;
    my $i = 0;

    # Create watcher timer
    my $watcher = AnyEvent->timer (after => 1, interval => 1, cb => sub {
        $quit->send unless $self->ok;
    });

    # Create process timer

eg/ghost_ioloop.pl  view on Meta::CPAN

#!/usr/bin/perl -w
use strict;

my $g = MyGhost->new(
    logfile => 'daemon.log',
    pidfile => 'daemon.pid',
);

exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, reload, status

1;

package MyGhost;

use parent 'Acme::Ghost';
use Mojo::IOLoop;

sub init {
    my $self = shift;
    $self->{loop} = Mojo::IOLoop->new;
}
sub startup {
    my $self = shift;
    my $loop = $self->{loop};
    my $i = 0;

    # Add a timers
    my $timer = $loop->timer(5 => sub {
        my $l = shift; # loop
        $self->log->info("Timer!");
    });
    my $recur = $loop->recurring(1 => sub {
        my $l = shift; # loop
        $l->stop unless $self->ok;
        $self->log->info("Tick! " . ++$i);
        $l->stop if $i >= 10;
    });

    $self->log->debug("Start IOLoop");

    # Start event loop if necessary
    $loop->start unless $loop->is_running;

    $self->log->debug("Finish IOLoop");
}

1;

__END__

eg/ghost_nobody.pl  view on Meta::CPAN

#!/usr/bin/perl -w
use strict;

my $g = MyGhost->new(
    pidfile => '/tmp/daemon.pid',
    user    => 'nobody',
    group   => 'nogroup',
);

exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, status

1;

package MyGhost;

use parent 'Acme::Ghost';

sub startup {
    my $self = shift;
    my $max = 100;
    my $i = 0;
    while ($self->ok) {
        $i++;
        sleep 3;
        $self->log->debug(sprintf("> %d/%d", $i, $max));
        last if $i >= $max;
    }
}

1;

__END__

sudo ACME_GHOST_DEBUG=1 perl -Ilib eg/ghost_nobody.pl start

eg/ghost_simple.pl  view on Meta::CPAN

#!/usr/bin/perl -w
use strict;

use Acme::Ghost;

my $g = Acme::Ghost->new(
    logfile => 'daemon.log',
    pidfile => 'daemon.pid',
);

my $cmd = shift(@ARGV) // 'start';
if ($cmd eq 'status') {
    if (my $runned = $g->status) {
        print "Running $runned\n";
    } else {
        print "Not running\n";
    }
    exit 0; # Ok
} elsif ($cmd eq 'stop') {
    if (my $runned = $g->stop) {
        if ($runned < 0) {
            print STDERR "Failed to stop " . $g->pid . "\n";
            exit 1; # Error
        }
        print "Stopped $runned\n";
    } else {
        print "Not running\n";
    }
    exit 0; # Ok
} elsif ($cmd ne 'start') {
    print STDERR "Command incorrect\n";
    exit 1; # Error
}

# Daemonize
$g->daemonize;

my $max = 10;
my $i = 0;
while (1) {

eg/prefork_acme.pl  view on Meta::CPAN

#!/usr/bin/perl -w
use strict;

my $g = MyGhost->new(
    logfile => 'daemon.log',
    pidfile => 'daemon.pid',
);
exit $g->ctrl(shift(@ARGV) // 'start');

1;

package MyGhost;

use parent 'Acme::Ghost::Prefork';
use Data::Dumper qw/Dumper/;

sub init {
    my $self = shift;

eg/prefork_acme.pl  view on Meta::CPAN

        sleep 1;
        $self->log->debug(sprintf("$$> %d/%d", $i, $max));
        last if $i >= $max;
    }
}

1;

__END__

perl -Ilib eg/prefork_acme.pl start

eg/prefork_ioloop.pl  view on Meta::CPAN

#!/usr/bin/perl -w
use strict;

my $g = MyGhost->new(
    logfile => 'daemon.log',
    pidfile => 'daemon.pid',
);
exit $g->ctrl(shift(@ARGV) // 'start');

1;

package MyGhost;

use parent 'Acme::Ghost::Prefork';
use Mojo::IOLoop;

sub init {
    my $self = shift;

eg/prefork_ioloop.pl  view on Meta::CPAN

    my $recur = $loop->recurring(1 => sub {
        my $l = shift; # loop
        $l->stop unless $self->tick;
        $self->log->debug(sprintf("$$> %d/%d", ++$i, $max));
        $l->stop if $i >= $max;
    });

    $self->log->debug("Start IOLoop");

    # Start event loop if necessary
    $loop->start unless $loop->is_running;

    $self->log->debug("Finish IOLoop");
}

1;

__END__

perl -Ilib eg/prefork_ioloop.pl start

lib/Acme/Ghost.pm  view on Meta::CPAN


List of LSB Daemon Control Methods

These methods can be used to control the daemon behavior.
Every effort has been made to have these methods DWIM (Do What I Mean),
so that you can focus on just writing the code for your daemon.

=head2 ctrl

    exit $g->ctrl( shift @ARGV, 'USR2' );
      # start, stop, restart, reload, status

Daemon Control Dispatcher with using USR2 to reloading

    exit $g->ctrl( shift @ARGV, 0 );

This example shows how to forced suppress reloading (disable send users signals to daemon)

=head2 reload

    $exit_code = $g->reload; # SIGHUP (by default)
    $exit_code = $g->reload('USR2'); # SIGUSR2
    $exit_code = $g->reload(12); # SIGUSR2 too
    say "Reloading ". $g->pid;

This method performs sending signal to Your daemon and return C<0> as exit code.
This method is primarily intended to perform a daemon reload

=head2 restart

    $exit_code = $g->restart;
    if ($exit_code) {
        say STDERR "Restart failed " . $g->pid;
    } else {
        say "Restart successful";
    }

This method performs restarting the daemon and returns C<0> as successfully
exit code or C<1> in otherwise

=head2 start

    my $exit_code = $g->start;
    say "Running ". $g->pid;
    exit $exit_code;

This method performs starting the daemon and returns C<0> as exit code.
The spawned process calls the startup handler and exits with status C<0>
as exit code without anything return

=head2 status

    if (my $runned = $g->status) {
        say "Running $runned";
    } else {
        say "Not running";
    }

lib/Acme/Ghost.pm  view on Meta::CPAN


=head2 init

    sub init {
        my $self = shift;
        # . . .
    }

The init() method is called after spawning (forking) and after daemonizing

=head2 startup

    sub startup {
        my $self = shift;
        # . . .
    }

The startup() method is called after daemonizing in service mode

This is your main hook into the service, it will be called at service startup.
Meant to be overloaded in a subclass.

=head2 cleanup

    sub cleanup {
        my $self = shift;
        my $scope = shift; # 0 or 1
        # . . .
    }

lib/Acme/Ghost.pm  view on Meta::CPAN

        my $self = shift;
        $self->log->debug(">> Hang up!");
    }

=head1 EXAMPLES

=over 4

=item ghost_simple.pl

This is traditional way to start daemons

    use Acme::Ghost;

    my $g = Acme::Ghost->new(
        logfile => 'daemon.log',
        pidfile => 'daemon.pid',
    );

    my $cmd = shift(@ARGV) // 'start';
    if ($cmd eq 'status') {
        if (my $runned = $g->status) {
            print "Running $runned\n";
        } else {
            print "Not running\n";
        }
        exit 0; # Ok
    } elsif ($cmd eq 'stop') {
        if (my $runned = $g->stop) {
            if ($runned < 0) {
                print STDERR "Failed to stop " . $g->pid . "\n";
                exit 1; # Error
            }
            print "Stopped $runned\n";
        } else {
            print "Not running\n";
        }
        exit 0; # Ok
    } elsif ($cmd ne 'start') {
        print STDERR "Command incorrect\n";
        exit 1; # Error
    }

    # Daemonize
    $g->daemonize;

    my $max = 10;
    my $i = 0;
    while (1) {

lib/Acme/Ghost.pm  view on Meta::CPAN


=item ghost_acme.pl

Simple acme example of daemon with reloading demonstration

    my $g = MyGhost->new(
        logfile => 'daemon.log',
        pidfile => 'daemon.pid',
    );

    exit $g->ctrl(shift(@ARGV) // 'start'); # start, stop, restart, reload, status

    1;

    package MyGhost;

    use parent 'Acme::Ghost';

    sub init {
        my $self = shift;
        $SIG{HUP} = sub { $self->hangup }; # Listen USR2 (reload)
    }
    sub hangup {
        my $self = shift;
        $self->log->debug("Hang up!");
    }
    sub startup {
        my $self = shift;
        my $max = 100;
        my $i = 0;
        while ($self->ok) {
            $i++;
            sleep 3;
            $self->log->debug(sprintf("> %d/%d", $i, $max));
            last if $i >= $max;
        }
    }

lib/Acme/Ghost.pm  view on Meta::CPAN


=item ghost_ioloop.pl

L<Mojo::IOLoop> example

    my $g = MyGhost->new(
        logfile => 'daemon.log',
        pidfile => 'daemon.pid',
    );

    exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, reload, status

    1;

    package MyGhost;

    use parent 'Acme::Ghost';
    use Mojo::IOLoop;

    sub init {
        my $self = shift;
        $self->{loop} = Mojo::IOLoop->new;
    }
    sub startup {
        my $self = shift;
        my $loop = $self->{loop};
        my $i = 0;

        # Add a timers
        my $timer = $loop->timer(5 => sub {
            my $l = shift; # loop
            $self->log->info("Timer!");
        });
        my $recur = $loop->recurring(1 => sub {
            my $l = shift; # loop
            $l->stop unless $self->ok;
            $self->log->info("Tick! " . ++$i);
            $l->stop if $i >= 10;
        });

        $self->log->debug("Start IOLoop");

        # Start event loop if necessary
        $loop->start unless $loop->is_running;

        $self->log->debug("Finish IOLoop");
    }

    1;

=item ghost_ae.pl

AnyEvent example

    my $g = MyGhost->new(
        logfile => 'daemon.log',
        pidfile => 'daemon.pid',
    );

    exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, reload, status

    1;

    package MyGhost;

    use parent 'Acme::Ghost';
    use AnyEvent;

    sub startup {
        my $self = shift;
        my $quit = AnyEvent->condvar;
        my $i = 0;

        # Create watcher timer
        my $watcher = AnyEvent->timer (after => 1, interval => 1, cb => sub {
            $quit->send unless $self->ok;
        });

        # Create process timer

lib/Acme/Ghost.pm  view on Meta::CPAN


        $self->log->debug("Start AnyEvent");
        $quit->recv; # Run!
        $self->log->debug("Finish AnyEvent");
    }

    1;

=item ghost_nobody.pl

This example shows how to start daemons over nobody user and logging to syslog (default)

    my $g = MyGhost->new(
        pidfile => '/tmp/daemon.pid',
        user    => 'nobody',
        group   => 'nogroup',
    );

    exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, status

    1;

    package MyGhost;

    use parent 'Acme::Ghost';

    sub startup {
        my $self = shift;
        my $max = 100;
        my $i = 0;
        while ($self->ok) {
            $i++;
            sleep 3;
            $self->log->debug(sprintf("> %d/%d", $i, $max));
            last if $i >= $max;
        }
    }

lib/Acme/Ghost.pm  view on Meta::CPAN

use POSIX qw/ :sys_wait_h SIGINT SIGTERM SIGQUIT SIGKILL SIGHUP SIG_BLOCK SIG_UNBLOCK /;

use Acme::Ghost::FilePid;
use Acme::Ghost::Log;

use constant {
    DEBUG       => $ENV{ACME_GHOST_DEBUG} || 0,
    IS_ROOT     => (($> == 0) || ($< == 0)) ? 1 : 0,
    SLEEP       => 60,
    INT_TRIES   => 3,
    LSB_COMMANDS=> [qw/start stop reload restart status/],
};

sub new {
    my $class = shift;
    my $args = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {};
    my $name = $args->{name} || File::Basename::basename($0);
    my $user = $args->{user} // '';
    my $group = $args->{group} // '';

    # Get UID by User

lib/Acme/Ghost.pm  view on Meta::CPAN

    # Child
    $self->{daemonized} = 1; # Set daemonized flag
    $self->filepid->pid($$)->save; # Set new PID and Write PID file
    chown($uid, $gid, $pid_file) if IS_ROOT && -e $pid_file;

    # Set GID and UID
    $self->set_gid->set_uid;

    # Turn process into session leader, and ensure no controlling terminal
    unless (DEBUG) {
        die "Can't start a new session: $!" if POSIX::setsid() < 0;
    }

    # Init logger!
    my $log = $self->log;

    # Close all standart filehandles
    unless (DEBUG) {
        my $devnull = File::Spec->devnull;
        open STDIN, '<', $devnull or die "Can't open STDIN from $devnull: $!\n";
        open STDOUT, '>', $devnull or die "Can't open STDOUT to $devnull: $!\n";

lib/Acme/Ghost.pm  view on Meta::CPAN

    return $self;
}
sub is_daemonized { shift->{daemonized} }
sub is_spirited { shift->{spirited} }
sub pid { shift->{pid} }

# Hooks
sub preinit { }
sub init { }
sub cleanup { } # 0 -- at destroy; 1 -- at interrupt
sub startup { }
sub hangup { }

# Process
sub flush { # Flush process counters
    my $self = shift;
    $self->{interrupt} = 0;
    $self->{signo} = 0;
    $self->{ok} = 1;
    return $self;
}

lib/Acme/Ghost.pm  view on Meta::CPAN

    $self->log->debug(sprintf("Request for terminate of ghost process %s received on signal %s", $self->pid, $signo));
    if ($self->{interrupt} >= INT_TRIES) { # Forced terminate
        POSIX::_exit(1) if $self->is_spirited;
        $self->cleanup(1);
        $self->log->fatal(sprintf("Ghost process %s forcefully terminated on signal %s", $self->pid, $signo));
        $self->filepid->remove;
        POSIX::_exit(1);
    }
    $self->{interrupt}++;
}
sub start {
    my $self = shift;
    $self->daemonize(1); # First daemonize and switch to child process
    return 0 unless $self->is_daemonized; # Exit from parent process

    # Signals Trapping for interruption
    local $SIG{INT}  = sub { $self->_term(SIGINT) };  # 2
    local $SIG{TERM} = sub { $self->_term(SIGTERM) }; # 15
    local $SIG{QUIT} = sub { $self->_term(SIGQUIT) }; # 3

    $self->flush; # Flush process counters
    $self->log->info(sprintf("Ghost process %s started", $self->pid));
    $self->startup(); # Master hook
    $self->log->info(sprintf("Ghost process %s stopped", $self->pid));
    exit 0; # Exit code for child: ok
}
sub stop {
    my $self = shift;
    my $pid = $self->filepid->running;
       $self->{pid} = $pid;
    return 0 unless $pid; # Not running

    # Try SIGQUIT ... 2s ... SIGTERM ... 4s ... SIGINT ... 3s ... SIGKILL ... 3s ... UNDEAD!

lib/Acme/Ghost.pm  view on Meta::CPAN

        return $pid;
    }

    # The ghost process doesn't seem to want to die. It is still running...;
    return -1 * $pid;
}
sub status {
    my $self = shift;
    return $self->{pid} = $self->filepid->running || 0;
}
sub restart {
    my $self = shift;
    my $runned = $self->stop;
    return 1 if $runned && $runned < 0; # It is still running
    _sleep(1); # delay before starting
    $self->start;
}
sub reload {
    my $self = shift;
    my $signo = shift // SIGHUP;
    $self->{pid} = $self->filepid->running || 0;
    return $self->start unless $self->pid; # Not running - start!
    kill $signo, $self->pid;
    return 0;
}
sub ctrl { # Dispatching
    my $self = shift;
    my $cmd = shift || '';
    my $sig = shift; # SIGHUP
    unless (grep {$cmd eq $_} @{(LSB_COMMANDS)}) {
        print STDERR "Command incorrect\n";
        return 1;
    }
    my $exit_code = 0; # Ok
    if ($cmd eq 'start') {
        $exit_code = $self->start;
        printf "Running %s\n", $self->pid;
    } elsif ($cmd eq 'status') {
        if (my $runned = $self->status) {
            printf "Running %s\n", $runned;
        } else {
            print "Not running\n";
        }
    } elsif ($cmd eq 'stop') {
        if (my $runned = $self->stop) {
            if ($runned < 0) {
                printf STDERR "The ghost process %s doesn't seem to want to die. It is still running...\n", $self->pid;
                $exit_code = 1;
            } else {
                printf "Stopped %s\n", $runned;
            }
        } else {
            print "Not running\n";
        }
    } elsif ($cmd eq 'restart') {
        $exit_code = $self->restart;
        if ($exit_code) {
            printf STDERR "Restart failed %s\n", $self->pid;
        } else {
            print "Restart successful\n";
        }
    } elsif ($cmd eq 'reload') {
        $exit_code = $self->reload($sig);
        printf "Reloading %s\n", $self->pid;
    }
    return $exit_code;
}

sub DESTROY {
    my $self = shift;

lib/Acme/Ghost/Prefork.pm  view on Meta::CPAN

amount of time you expect any one operation to block the event loop

Defaults to C<50>

=head2 spare

    spare => 2

Temporarily spawn up to this number of additional spirits if there is a need

This allows for new spirits to be started while old ones are still shutting down gracefully,
drastically reducing the performance cost of spirit restarts.

Defaults to C<2>

=head2 spirits, workers

    spirits => 4

Number of spirit processes.

A good rule of thumb is two spirit processes per CPU core for applications that perform mostly

lib/Acme/Ghost/Prefork.pm  view on Meta::CPAN

This method is called immediately after creating the instance and returns it

B<NOTE:> Internal use only!

=head2 healthy

    my $healthy = $g->healthy;

This method returns the number of currently active live spirit processes (with a heartbeat)

=head2 startup

    $prefork->startup;

This method starts preforked process (manager and spirits) and wait for L</"MANAGER SIGNALS">

=head2 tick

    my $ok = $g->tick;
    my $ok = $g->tick(1); # marks the finished status

This is B<required> method of spirit main process that sends heartbeat message to
process manager and returns the status of the running server via the 'ok' attribute

=head1 MANAGER SIGNALS

lib/Acme/Ghost/Prefork.pm  view on Meta::CPAN

        my $self = shift;
        my $pid = shift;
        # . . .
    }

Is called when a spirit process is spawned

    sub spawn {
        my $self = shift;
        my $pid = shift;
        $self->log->debug("Spirit $pid started");
    }

=head2 waitup

    sub waitup {
        my $self = shift;
        # . . .
    }

Is called when the manager starts waiting for new heartbeat messages

    sub waitup {
        my $self = shift;
        my $spirits = $prefork->{spirits};
        $self->log->debug("Waiting for heartbeat messages from $spirits spirits");
    }

=head2 spirit

B<The spirit body>

This hook is called when the spirit process has started and is ready to run in isolation.
This is main hook that MUST BE implement to in user subclass

    sub spirit {
        my $self = shift;
        # . . .
    }

=head1 EXAMPLES

=over 4

=item prefork_acme.pl

Prefork acme example of daemon with reloading demonstration

    my $g = MyGhost->new(
        logfile => 'daemon.log',
        pidfile => 'daemon.pid',
    );
    exit $g->ctrl(shift(@ARGV) // 'start');

    1;

    package MyGhost;

    use parent 'Acme::Ghost::Prefork';
    use Data::Dumper qw/Dumper/;

    sub init {
        my $self = shift;

lib/Acme/Ghost/Prefork.pm  view on Meta::CPAN

    1;

=item prefork_ioloop.pl

L<Mojo::IOLoop> example

    my $g = MyGhost->new(
        logfile => 'daemon.log',
        pidfile => 'daemon.pid',
    );
    exit $g->ctrl(shift(@ARGV) // 'start');

    1;

    package MyGhost;

    use parent 'Acme::Ghost::Prefork';
    use Mojo::IOLoop;
    use Data::Dumper qw/Dumper/;

    sub init {

lib/Acme/Ghost/Prefork.pm  view on Meta::CPAN

        my $recur = $loop->recurring(1 => sub {
            my $l = shift; # loop
            $l->stop unless $self->tick;
            $self->log->debug(sprintf("$$> %d/%d", ++$i, $max));
            $l->stop if $i >= $max;
        });

        $self->log->debug("Start IOLoop");

        # Start event loop if necessary
        $loop->start unless $loop->is_running;

        $self->log->debug("Finish IOLoop");
    }

    1;

=back

=head1 TO DO

lib/Acme/Ghost/Prefork.pm  view on Meta::CPAN

    $self->{writer}             = undef; # Writable pipe to send messages to manager
    $self->{spare}              = $args{spare} || SPARE;
    $self->{spirits}            = $args{spirits} || $args{workers} || SPIRITS;
    $self->{heartbeat_interval} = $args{heartbeat_interval} || HEARTBEAT_INTERVAL;
    $self->{heartbeat_timeout}  = $args{heartbeat_timeout} || HEARTBEAT_TIMEOUT;
    $self->{graceful_timeout}   = $args{graceful_timeout} || GRACEFUL_TIMEOUT;
    $self->{spirit_cb}          = $args{spirit};

    return $self;
}
sub startup {
    my $self = shift;

    # Pipe for spirit communication
    pipe($self->{reader}, $self->{writer}) or croak("Can't create pipe: $!\n");

    # Set manager signals
    local $SIG{INT}  = local $SIG{TERM} = sub { $self->_stop };
    local $SIG{QUIT} = sub { $self->_stop(1) };
    local $SIG{CHLD} = sub { while ((my $pid = waitpid -1, WNOHANG) > 0) { $self->_stopped($pid) } };
    local $SIG{TTIN} = sub { $self->_increase };
    local $SIG{TTOU} = sub { $self->_decrease };

    # Starting
    $self->log->info("Manager $$ started");
    $self->{running} = 1;
    $self->_manage while $self->{running};
    $self->log->info("Manager $$ stopped");
}
sub healthy {
    return scalar grep { $_->{healthy} } values %{shift->{pool}};
}
sub tick { # Spirit level
    my $self = shift;
    my $finished = shift || 0; # 0 - no finished; 1 - finished
    $self->_heartbeat($finished);
    return $self->ok;
}

# User hooks
sub finish { }      # Emitted when the server shuts down
sub heartbeat { }   # Emitted when a heartbeat message has been received from a spirit
sub reap { }        # Emitted when a child process exited
sub spawn { }       # Emitted when a spirit process is spawned
sub waitup { }      # Emitted when the manager starts waiting for new heartbeat messages
sub spirit {
    my $self = shift;
    my $cb = $self->{spirit_cb};
    return unless $cb;
    return $self->$cb if ref($cb) eq 'CODE';
    $self->log->error("Callback `spirit` is incorrect");
    $self->tick(1);
}

# Internal methods

lib/Acme/Ghost/Prefork.pm  view on Meta::CPAN

    my $interval = $self->{heartbeat_interval};
    my $hb_to    = $self->{heartbeat_timeout};
    my $gf_to    = $self->{graceful_timeout};
    my $now      = Time::HiRes::time;
    my $log      = $self->log;
    for my $pid (keys %{$self->{pool}}) {
        next unless my $w = $self->{pool}{$pid}; # Get spirit struct

        # No heartbeat (graceful stop)
        if (!$w->{graceful} && ($w->{time} + $interval + $hb_to <= $now)) {
            $log->error("Spirit $pid has no heartbeat ($hb_to seconds), restarting");
            $w->{graceful} = $now;
        }

        # Graceful stop with timeout
        my $graceful = $w->{graceful} ||= $self->{gracefully_stop} ? $now : undef;
        if ($graceful && !$w->{attempt}) {
            $w->{attempt}++;
            $log->info("Stopping spirit $pid gracefully ($gf_to seconds)");
            kill 'QUIT', $pid or $self->_stopped($pid);
        }

lib/Acme/Ghost/Prefork.pm  view on Meta::CPAN

        $self->log->warn("Spirit $$ received QUIT signal") if DEBUG;
        $self->_heartbeat(1); # Send finish command to manager
    };

    # Close reader pipe
    delete $self->{reader};

    # Reset the random number seed for spirit
    srand;

    $self->log->info("Spirit $$ started");

    # Start spirit
    $self->spirit;

    exit 0; # EXIT FROM APPLICATION
}
sub _wait { # Manager level
    my $self = shift;

    # Call waitup hook



( run in 0.313 second using v1.01-cache-2.11-cpan-0d8aa00de5b )