CatalystX-Restarter-GTK
view release on metacpan or search on metacpan
lib/CatalystX/Restarter/GTK.pm view on Meta::CPAN
my $self = shift;
my $pid = shift;
$self->_child($pid);
# Detect server process termination.
my $server_watcher = AnyEvent->child(
pid => $self->_child,
cb => sub {
$self->notify_win('stopped');
$self->_child(0);
}
);
$self->server_watcher($server_watcher);
}
sub run_and_watch {
my ($self) = @_;
my $sem = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRWXU | IPC_CREAT)
or croak "Can not create semaphore $!";
my $sentry = Object::Destroyer->new($sem, 'remove');
socketpair(my $parent_sock, my $win_sock, AF_UNIX, SOCK_STREAM, 0)
or croak "socketpair failed: $!";
# Fork GUI process
my $pid = fork;
croak $! unless defined $pid;
if ($pid) {
close $win_sock;
$parent_sock->autoflush(1);
require AnyEvent;
$self->win_pid($pid);
$self->parent_sock($parent_sock);
# Detect window process termination
my $child_win = AnyEvent->child(
pid => $self->win_pid,
cb => sub {
$self->win_pid(0);
$self->_kill_child;
exit;
}
);
# Handle USR1 (Restart signal) from window
my $restart_watcher = AnyEvent->signal(
signal => SIGUSR1,
cb => sub {
$self->_kill_child;
$self->_fork_and_start;
}
);
if ($self->auto_restart) {
my $timer = AnyEvent->timer(
after => 1,
interval => 1,
cb => sub {
if (my @events = $self->_watcher->new_events) {
$self->_handle_events(@events);
}
}
);
}
# wait until window process sets up watchers.
$sem->op(0, -1, 0);
$sentry = undef;
$self->_fork_and_start;
# Wait for events infinitely.
AnyEvent->condvar->recv;
}
else {
$sentry->dismiss;
close $parent_sock;
$win_sock->autoflush(1);
# Use event loop of Gtk2 by loading it first.
require Gtk2;
Gtk2->init;
require AnyEvent::Socket;
my $win = WinMonitor->new($self->application_name);
$win->set_restart_handler(sub { kill SIGUSR1, getppid; });
my ($watcher, $start_timer);
# Creates event watcher for checking socket readiness of forked server.
$start_timer = sub {
$watcher = AnyEvent->timer(
after => 1,
cb => sub {
AnyEvent::Socket::tcp_connect('localhost', $self->port, sub {
if (shift) {
$watcher = undef;
$win->set_status('started');
}
else {
# Restart timer upon failure
$watcher = $start_timer->();
}
});
}
);
};
# SIGUSR1 - starting server
my $usr1_watcher = AnyEvent->signal(
signal => SIGUSR1,
cb => sub {
$win->clear_msg;
$win->set_status('starting');
$win_sock->say('1');
$start_timer->();
}
);
# SIGUSR2 - Server exited / killed
my $usr2_watcher = AnyEvent->signal(
signal => SIGUSR2,
cb => sub {
$win->set_status('stopped');
$watcher = undef;
$win_sock->say('1');
}
);
my $winsock_watcher = AnyEvent->io(
fh => $win_sock,
poll => 'r',
cb => sub {
# Unbuffered read from socket
return unless sysread($win_sock, my $msg, 256, 0);
$win->append_msg($msg);
}
);
$sem->op(0, 1, 0);
main Gtk2;
exit(0);
}
}
# Sends server status signal to window process.
{
my %map = ('starting' => SIGUSR1, 'stopped' => SIGUSR2);
sub notify_win {
my ($self, $msg) = @_;
return unless exists $map{$msg};
if ($self->win_pid) {
kill $map{$msg}, $self->win_pid;
# Wait until signal is handled. This is for synchronizing signals.
$self->parent_sock->getline;
}
}
}
sub _fork_and_start {
my $self = shift;
pipe(my $reader, my $writer) or croak "$!";
my $sem = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRWXU | IPC_CREAT)
or croak "failed to create semaphore $!";
my $sentry = Object::Destroyer->new($sem, 'remove');
my $pid = fork;
return unless (defined $pid);
if($pid) {
close $writer;
( run in 1.988 second using v1.01-cache-2.11-cpan-f56aa216473 )