--- /dev/null
+package Tak::Daemon::ListenerService;
+
+use Scalar::Util qw(weaken);
+use Moo;
+
+with 'Tak::Role::Service';
+
+has socket_location => (is => 'ro', required => 1);
+has router => (is => 'ro', required => 1);
+
+has state => (is => 'rw', default => sub { 'down' }, init_arg => undef);
+
+has _start_in_progress => (is => 'lazy', clearer => '_clear_start_in_progress');
+
+has listener => (is => 'rw', clearer => 'clear_listener');
+
+has connections => (is => 'ro', default => sub { {} });
+
+sub start_start_request {
+ my ($self, $req) = @_;
+ $req->result('already_started') if $self->state eq 'running';
+ push(@{$self->_start_in_progress->{requests}}, $req);
+ $self->_start_in_progress->{start}();
+}
+
+sub _build__start_in_progress {
+ my ($self) = @_;
+ weaken($self);
+ my %start = (requests => (my $requests = []));
+ $start{start} = sub {
+ $self->state('starting');
+ Tak->loop_upgrade;
+ Tak->loop->listen(
+ addr => {
+ family => "unix",
+ socktype => "stream",
+ path => $self->socket_location,
+ },
+ on_notifier => sub {
+ $self->listener($_[0]);
+ $_->success('started') for @$requests;
+ $self->_clear_start_in_progress;
+ $self->state('running');
+ },
+ on_resolve_error => sub { # no-op until we add non-unix
+ $_->failure(resolve => @_) for @$requests;
+ $self->_clear_start_in_progress;
+ $self->state('stopped');
+ },
+ on_listen_error => sub {
+ $_->failure(listen => @_) for @$requests;
+ $self->_clear_start_in_progress;
+ $self->state('stopped');
+ },
+ on_accept => sub {
+ $self->setup_connection($_[0]);
+ },
+ );
+ $start{start} = sub {}; # delete yourself
+ };
+ \%start;
+}
+
+sub handle_stop {
+ my ($self) = @_;
+ return 'already_stopped' if $self->state eq 'down';
+ # there's probably something more intelligent to do here, but meh
+ die failure => 'starting' if $self->state eq 'starting';
+ Tak->loop->remove($self->clear_listener);
+ $self->state('down');
+ return 'stopped';
+}
+
+sub DEMOLISH {
+ my ($self, $gd) = @_;
+ return if $gd;
+ if (my $l = $self->listener) {
+ $l->get_loop->remove($l);
+ }
+}
+
+sub setup_connection {
+ my ($self, $socket) = @_;
+ my $conn_set = $self->connections;
+ my $connection;
+ $connection = Tak::ConnectionService->new(
+ read_fh => $socket, write_fh => $socket,
+ listening_service => $self->router,
+ on_close => sub { delete $conn_set->{$connection} }
+ );
+ $conn_set->{$connection} = $connection;
+ return;
+}
+
+1;