Skip Menu |
Report information
Id: 126425
Status: open
Priority: 0/
Queue: perl6

Owner: Nobody
Requestors: rob [at] hoelz.ro
Cc:
AdminCc:

Severity: (no value)
Tag: (no value)
Platform: (no value)
Patch Status: (no value)
VM: Moar



Subject: Proc::Async methods can have unpredictable and nondeterministic order
Download (untitled) / with headers
text/plain 1.5k
Consider the following two programs: parent.pl: my $child = Proc::Async.new('perl6', 'child.pl', :w); $child.stdout.tap: { .say }; $child.print: "1\n"; $child.kill: SIGINT; $child.print: "2\n"; child.pl: signal(SIGINT).act: { say 'SIGINT'; exit } my $ = get(); say 'one'; my $ = get(); say 'two'; say 'done'; There are seven Proc::Async related events that occur when you run parent.pl: - "1\n" is sent to the child on standard input. - "one\n" is received from the child on standard output. - SIGINT is sent to the child. - "SIGINT\n" is received from the child on standard output. - "2\n" is sent to the child on standard input. - "two\n" is received from the child on standard output. - "done\n" is received from the child on standard output. If this program behaved correctly, not all of these events would occur, but due to the way these methods are implemented, the ordering may change. 7! is 5040, but I wrote a little program to prune orderings that just can't happen (ex. the child will always receive "1\n" before "2\n"). That program indicates that there are 42 possible orderings; I haven't observed so many when running this program, but I have observed a few. I understand that Proc::Async is asynchronous in all its dealings (hence the name), but the way the code looks feels very synchronous. Either the nondeterministic orderings should be fixed, or maybe the API should change to reflect that here be dragons. This affects S17-procasync/kill.t.
Download (untitled) / with headers
text/plain 1.9k
On Wed Oct 21 20:03:30 2015, rob@hoelz.ro wrote: Show quoted text
> Consider the following two programs: > > parent.pl: > > my $child = Proc::Async.new('perl6', 'child.pl', :w); > $child.stdout.tap: { .say }; > $child.print: "1\n"; > $child.kill: SIGINT; > $child.print: "2\n"; > > child.pl: > > signal(SIGINT).act: { say 'SIGINT'; exit } > > my $ = get(); > say 'one'; > my $ = get(); > say 'two'; > say 'done'; > > There are seven Proc::Async related events that occur when you run > parent.pl: > > - "1\n" is sent to the child on standard input. > - "one\n" is received from the child on standard output. > - SIGINT is sent to the child. > - "SIGINT\n" is received from the child on standard output. > - "2\n" is sent to the child on standard input. > - "two\n" is received from the child on standard output. > - "done\n" is received from the child on standard output. > > If this program behaved correctly, not all of these events would > occur, but due to the way these methods are implemented, the ordering > may change. 7! is 5040, but I wrote a little program to prune > orderings that just can't happen (ex. the child will always receive > "1\n" before "2\n"). That program indicates that there are 42 > possible orderings; I haven't observed so many when running this > program, but I have observed a few. > > I understand that Proc::Async is asynchronous in all its dealings > (hence the name), but the way the code looks feels very synchronous. > Either the nondeterministic orderings should be fixed, or maybe the > API should change to reflect that here be dragons. > > This affects S17-procasync/kill.t.
Sorry, but this is too vague for a bug report. "feels very synchronous" is vague, and it's not clear to me what needs to happen to close this ticket. Note that write, print and say returns promises which you can await on to enforce ordering. Please give some actual examples for things you consider bugs. Otherwise I'll close the ticket.
On 2015-10-21 21:41:40, moritz wrote: Show quoted text
> On Wed Oct 21 20:03:30 2015, rob@hoelz.ro wrote:
> > Consider the following two programs: > > > > parent.pl: > > > > my $child = Proc::Async.new('perl6', 'child.pl', :w); > > $child.stdout.tap: { .say }; > > $child.print: "1\n"; > > $child.kill: SIGINT; > > $child.print: "2\n"; > > > > child.pl: > > > > signal(SIGINT).act: { say 'SIGINT'; exit } > > > > my $ = get(); > > say 'one'; > > my $ = get(); > > say 'two'; > > say 'done'; > > > > There are seven Proc::Async related events that occur when you run > > parent.pl: > > > > - "1\n" is sent to the child on standard input. > > - "one\n" is received from the child on standard output. > > - SIGINT is sent to the child. > > - "SIGINT\n" is received from the child on standard output. > > - "2\n" is sent to the child on standard input. > > - "two\n" is received from the child on standard output. > > - "done\n" is received from the child on standard output. > > > > If this program behaved correctly, not all of these events would > > occur, but due to the way these methods are implemented, the ordering > > may change. 7! is 5040, but I wrote a little program to prune > > orderings that just can't happen (ex. the child will always receive > > "1\n" before "2\n"). That program indicates that there are 42 > > possible orderings; I haven't observed so many when running this > > program, but I have observed a few. > > > > I understand that Proc::Async is asynchronous in all its dealings > > (hence the name), but the way the code looks feels very synchronous. > > Either the nondeterministic orderings should be fixed, or maybe the > > API should change to reflect that here be dragons. > > > > This affects S17-procasync/kill.t.
> > Sorry, but this is too vague for a bug report. "feels very > synchronous" is vague, and it's not clear to me what needs to happen > to close this ticket. > > Note that write, print and say returns promises which you can await on > to enforce ordering. > > Please give some actual examples for things you consider bugs. > Otherwise I'll close the ticket.
To me, this feels like a violation of "Similar things should look similar, different things should like different". If I'm a newbie, and I see this: $child.print: "1\n"; $child.kill: SIGINT; $child.print: "2\n"; I will probably think "this is going to print '1' to the child, then send it SIGINT, then print '2' to the child. Unless you wait for the promises (which is the right thing to do here), you have several potential orderings of these three operations: - print(1), kill(SIGINT), print(2) - print(1), print(2), kill(SIGINT) - kill(SIGINT), print(1), print(2) (The other three permutations have print(2) before print(1), which can't happen with the current implementation, so I have omitted them) So the problem (to me) is that this code reads like synchronous code, and I expected it to act with a deterministic order like synchronous code would. So I think the bug/resolution is one of the following: 1) The order of operations is nondeterministic - resolution would be to make them deterministic. 2) The method calls look like synchronous method calls - resolution would be to change the names so it's obvious that they're synchronous and their order is not guaranteed, as well as what's required for resolution #3 below. 3) The example program (and S17-procasync/kill.t) is relying on undefined behavior (the order of Proc::Async operations) - resolution would be to update kill.t to await the promises to enforce the expected order. My issue with #3 is that strange timing bugs can lurk in code that looks correct when you read it, and it's also easy to write code that conceals those same strange timing bugs. One solution for this would be to have print and friends fail in sink context (which strikes me as a bit magical), forcing you to acknowledge the Promise, but that would make the case where the user really *doesn't* care about operation order annoying to code. I personally favor #2, changing print → async-print or something along those lines.


This service is sponsored and maintained by Best Practical Solutions and runs on Perl.org infrastructure.

For issues related to this RT instance (aka "perlbug"), please contact perlbug-admin at perl.org