Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Perl6: react/whenever timeout

by OneTrueDabe (Acolyte)
on Jul 20, 2016 at 03:56 UTC ( #1168103=perlquestion: print w/replies, xml ) Need Help??

OneTrueDabe has asked for the wisdom of the Perl Monks concerning the following question:

As an exercise in enlightenment, I've been going through Rob Pike's "Go Concurrency Patterns" video, trying to rewrite his examples in Perl 6.

Where I'm having trouble, though, is getting Channel.receive to timeout per request. (I can easily make a global timeout for all communication, but I want the timeout to reset after each message comes through.)

The following works, but I feel like I should be using something "react/whenever" or something, to make it more idiomatic.

PS — Yes, I know, "There's More Than One Way To Do It" (and "If It's Stupid, But It WORKS, It Isn't Stupid!") If this were an actual application, I'd be happy to leave it "as is"; I'm just trying to understand the "More Than One" ways... «grin»

#!/usr/bin/env perl6 sub channel (Str $msg --> Channel) { my $c = Channel.new; start { for ^Inf -> $i { my $rand = (^2e3).pick / 1000; $c.send( "$msg $i = $rand ms" ); sleep $rand; # 0–2 seconds } } return $c; } my $c = channel("Message"); say "Listening"; my $timeout = Promise.in(1.75); loop { if $c.poll -> $item { say qq|Got: "$item"|; # Reset Timeout $timeout = Promise.in(1.75); # Exit if channel takes > 1.75 seconds } elsif $timeout { say "Timeout"; last } } say "Finished";
However, if I change that to:
react { whenever $c -> $item { say qq|Got: "$item"| } whenever Promise.in(5) { say "Timeout"; done } }
... it always runs for ~five seconds, and then exits. (You'd think it would never timeout, since the channel is set to send at least one message every two seconds.)

Any idea what I'm missing?

[Edit: Refactored "timeout" channel with Promise.in()]

Replies are listed 'Best First'.
Re: Perl6: react/whenever timeout
by moritz (Cardinal) on Jul 21, 2016 at 20:57 UTC

    Concurrency is not my big strength, so this will likely turn out to be a bit more clunky than necessary.

    One possible trick is to use the Promise.anyof combinator to get the first of the timeout or the actual receiving action:

    my $c = channel("Message"); say "Listening"; loop { my $timeout = Promise.in(1.75); my $result = start { $c.receive } my $combined = Promise.anyof( $result , $timeout); await $combined.then({ if $result { say $result.result; } else { say 'timeout'; } }) }

    This puts a timeout on every individual .receive, but finishes early if a value is available earlier. It's also not a busy loop, so doesn't use much CPU.

    I'm sure there are much more elegant solutions out there if you use a supply instead of a channel to generate the values; maybe some of the promise combinators like zip-latest can be used then.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1168103]
Approved by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (8)
As of 2020-07-07 08:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?