perlasyncsocketunix-socketio-socket

how to remove a IO::Async::Listener (or its notifier) object from a IO::Async::Loop event in perl


I have a piece of code that creates a UNIX domain socket using IO::Socket::UNIX and gives it to an instance of IO::Async::Listener to handle listening on the socket and notifying on receiving data. The IO::Async::Listener, then, is added to a IO::Async::Loop event loop instance. The sockets are created dynamically in a controlled manager, of course.

On a certain condition, I'd like to remove the socket from the event loop (completely delete it, or temporarily disable it on other conditions if possible) but I don't know how.

IO::Async::Loop offers to remove IO::Async::Notifier objects from the event loop via $loop->remove( $notifier ) but creating the notifier was handled internally by IO::Async::Listener (via IO::Async::Stream, I presume?). Even on Ctrl-C of my script, the socket file is not deleted, do I just have to manually close $socket and unlink( $path ) of the socket file?

Here's an abstract code of the desired behavior:

#!/usr/bin/perl

use IO::Async::Loop;
use IO::Async::Listener;
use IO::Socket::UNIX;

my $loop = IO::Async::Loop->new;
my $listener = IO::Async::Listener->new(
                    on_stream => sub {
                            my ( undef, $stream ) = @_;

                            $stream->configure(
                                    on_read => sub {
                                            my ( $self, $buffref, $eof ) = @_;
                                            $self->write( $buffref );
                                            $buffref = "";
                                            return 0;
                                    },
                            );

                            $loop->add( $stream );
                    },
            );
$loop->add( $listener );

my $socket = IO::Socket::UNIX->new(
            Local => "echo.sock",
            Listen => 1,
            ) || die "Cannot make UNIX socket - $!\n";

$listener->listen(
            handle => $socket,
            );

my $condition = true;

while($condition) {
            // this is probably wrong
            $loop->remove( $listener );
            $condition = false;
}

Solution

  • You seem to have two related questions here.

    You can remove the listener object from the loop by using the loop's remove method:

    $loop->remove( $listener )
    

    However, removing the listener from the loop won't unlink the socket node from the fileystem. For that you will need the unlink code you suggested.

    Personally, in such code as creates sockets like this, I make use of an END block:

    my $path = "echo.sock";
    my $socket = IO::Socket::UNIX->new(
                Local => $path,
                Listen => 1,
                ) || die "Cannot make UNIX socket - $!\n";
    END { $socket and unlink $path }
    $SIG{INT} = $SIG{TERM} = sub { exit 1 };
    

    The $SIG line is required to ensure that SIGINT and SIGTERM still run the END block, rather than just causing the perl process to immediately terminate.

    Finally, you should note that you can use a neater form of the listen method, rather than explicitly creating the UNIX socket in your case, you can just

    my $listener = ...
    $loop->add( $listener );
    
    $listener->listen(
      addr => {
        family   => "unix",
        socktype => "stream",
        path     => "echo.sock",
      },
    );
    

    Though again in this case you will still need the END block.