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 )