Skip Menu |

This queue is for tickets about the IO-Async CPAN distribution.

Report information
The Basics
Id: 67883
Status: resolved
Priority: 0/
Queue: IO-Async

People
Owner: Nobody in particular
Requestors: TEAM [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Unimportant
Broken in: 0.40
Fixed in: 0.41



Subject: Zero-length writes raise an exception
Hi, As mentioned briefly in IRC: Calling IO::Async::Stream->write('') will raise 'Internal consistency problem - empty writequeue item without a gensub', which is perfectly reasonable but the wording is perhaps not immediately obvious. Simple test case attached to demonstrate the current behaviour. For a normal ->write with no other parameters, ignoring the request might be acceptable, although if the on_flush callback is provided then perhaps it should raise an error ("on_flush callback given but no data was provided to ->write"?), or include the on_flush with the previous ->write entry. Since I was just using on_flush as a way of tracking buffer status, ended up replacing the write with this instead: $stream->write($buffer, on_flush => ...) if length $buffer; Alternatively, maybe could just include a short documentation note for IO::Async::Stream->write explaining that an empty write buffer will raise an error? thanks, Tom
Subject: empty-write.t
use strict; use warnings; use Test::More tests => 2; use IO::Async::Loop; use IO::Async::Stream; use Test::Exception; my $loop = IO::Async::Loop->new; my $stream = IO::Async::Stream->new_for_stdout; $loop->add($stream); $stream->write(''); $loop->later(sub { $loop->loop_stop; }); # Fails with 'Internal consistency problem - empty writequeue item without a gensub' lives_ok { $loop->loop_forever; } 'loop_forever runs without complaints'; $stream->write( '', on_flush => sub { pass('Flushed OK'); $loop->loop_stop; } ); $loop->later(sub { $loop->loop_stop; }); # Fails with 'Internal consistency problem - empty writequeue item without a gensub' lives_ok { $loop->loop_forever; } 'loop_forever works with on_flush as well';
On Sun May 01 11:42:10 2011, TEAM wrote: Show quoted text
> Calling IO::Async::Stream->write('') will raise 'Internal consistency > problem - empty writequeue item without a gensub', which is perfectly > reasonable but the wording is perhaps not immediately obvious.
Good catch. Show quoted text
> Alternatively, maybe could just include a short documentation note for > IO::Async::Stream->write explaining that an empty write buffer will > raise an error?
Done better. Fixed the implementation so it specifically allows an empty string; queueing another "on_flush" callback at that point in the queue. See attached patch. -- Paul Evans
Subject: rt67883.patch
=== modified file 'lib/IO/Async/Stream.pm' --- lib/IO/Async/Stream.pm 2011-04-14 15:23:21 +0000 +++ lib/IO/Async/Stream.pm 2011-05-19 14:25:04 +0000 @@ -361,7 +361,7 @@ my $head = $self->{writequeue}[WQ_DATA]; - if( !length $head->[WQ_DATA] ) { + if( !defined $head->[WQ_DATA] ) { my $gensub = $head->[WQ_GENSUB] or die "Internal consistency problem - empty writequeue item without a gensub\n"; $head->[WQ_DATA] = $gensub->( $self ); @@ -390,9 +390,14 @@ substr( $head->[WQ_DATA], 0, $len ) = ""; - if( !length $head->[WQ_DATA] and !$head->[WQ_GENSUB] ) { - $head->[WQ_ON_FLUSH]->( $self ) if $head->[WQ_ON_FLUSH]; - shift @{ $self->{writequeue} }; + if( !length $head->[WQ_DATA] ) { + if( $head->[WQ_GENSUB] ) { + undef $head->[WQ_DATA]; # We'll get some more next time around + } + else { + $head->[WQ_ON_FLUSH]->( $self ) if $head->[WQ_ON_FLUSH]; + shift @{ $self->{writequeue} }; + } } return 1; @@ -501,6 +506,11 @@ C<write_handle>, then calls to the C<write> method will simply queue the data and return. It will be flushed when the object is added to the loop. +If C<$data> is a defined but empty string, the write is still queued, and the +C<on_flush> continuation will be invoked, if supplied. This can be used to +obtain a marker, to invoke some code once the output queue has been flushed up +to this point. + =cut sub write @@ -535,7 +545,6 @@ push @{ $self->{writequeue} }, my $elem = []; if( ref $data eq "CODE" ) { - $elem->[WQ_DATA] = ""; $elem->[WQ_GENSUB] = $data; } else { === modified file 't/21stream-2write.t' --- t/21stream-2write.t 2011-02-10 21:37:20 +0000 +++ t/21stream-2write.t 2011-05-19 14:09:27 +0000 @@ -4,7 +4,7 @@ use IO::Async::Test; -use Test::More tests => 39; +use Test::More tests => 40; use Test::Refcount; use POSIX qw( EAGAIN ECONNRESET ); @@ -83,6 +83,12 @@ is( read_data( $rd ), "hello again\n", 'flushed data does get flushed' ); + $flushed = 0; + $stream->write( "", on_flush => sub { $flushed++ } ); + wait_for { $flushed }; + + ok( 1, "write empty data with on_flush" ); + my $done; $stream->write(