Search code examples
reactive-programmingraku

When `whenever` block in `react` section starts processing events?


Let's say I have simple socket listening in one thread and I want to hold another thread until port is opened and socket is actually listening. Which one is correct:

  1. Using do? Documentation of do says The simplest way to run a block, but body of whenever is not actually ran until something connects.
my $p = Promise.new;
start {
    react {
        $p.keep: do whenever IO::Socket::Async.listen( "127.0.0.1", 4528 ) -> $c { ... }
    }
};

await $p;
  1. Following code order? Is it guaranteed that after whenever block Supply used in that block got access to scheduler and is already working?
my $p = Promise.new;
start {
    react {
        whenever IO::Socket::Async.listen( "127.0.0.1", 4528 ) -> $c { ... }
        $p.keep;
    }
};

await $p;
  1. Using phaser? Documentation of INIT is a bit unclear. It says it should be ran at runtime ASAP. So it looks like perfect candidate that guarantees block is initialized. But why it does not have access to defined Promise?
my $p = Promise.new;
start {
    react {
        whenever IO::Socket::Async.listen( "127.0.0.1", 4528 ) -> $c {
            INIT $p.keep; # ERROR, variable undefined
        }
    }
};

await $p;

Solution

  • None of the above. :-)

    An INIT block is run immediately after module load, and once in the lifetime of the program. It's typically for doing initialization work related to a module as a whole. So it would run before the start or react have even happened.

    Option 2 is closer, but not quite there. The Promise will be kept after the request to start listening has been issued, but that's an asynchronous operation, so there's a race.

    Option 1 is wrong, but going in an interesting direction. The whenever construct establishes a subscription, and evaluates to the Tap object that represents the subscription. Typically one doesn't care for this object, because the react/supply construct does all of the subscription management. However, some kinds of Supply return more interesting Tap objects than others. The IO::Async::Socket.listen method returns an IO::Socket::Async::ListenSocket.

    Among the methods on it is socket-port, which is a Promise that is kept with the port the server is listening on. The most common use of this is when you request the server listen on whatever port is free and want to discover which port was picked, but you can also use it to find out when the server is certainly listening.

    Thus the solution is:

    my $p = Promise.new;
    start {
        react {
            my $server = do whenever IO::Socket::Async.listen( "127.0.0.1", 4528 ) -> $c { ... }
            whenever $server.socket-port {
                $p.keep;
            }
        }
    };
    
    await $p;