Skip Menu |

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

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

People
Owner: Nobody in particular
Requestors: leonerd-cpan [...] leonerd.org.uk
Cc: berle [...] cpan.org
AdminCc:

Bug Information
Severity: Wishlist
Broken in: 0.49
Fixed in: 0.50



Subject: Make Heap an optional dependency
Heap is used by Ia::Internals::TimeQueue to implement an efficient queue of pending timer events. Without it, we could write a trivial but slightly less efficient implementation using an array, at a reschedule cost of O(n) rather than O(n log n). By providing a two implementations and detecting at load time, we can make this an optional dependency for smaller installs, but still allow larger systems to take advantage of more efficient timers simply by installing it. -- Paul Evans
On Tue May 29 08:21:09 2012, PEVANS wrote: Show quoted text
> Heap is used by Ia::Internals::TimeQueue to implement an efficient queue > of pending timer events. Without it, we could write a trivial but > slightly less efficient implementation using an array, at a reschedule > cost of O(n) rather than O(n log n).
Now patched as attached. Could be made possibly slightly more efficient by using a binary search + splice to insert the new elements, but would require benchmarking to see if that's actually better than the simple sort. -- Paul Evans
Subject: rt77520.patch
=== modified file 'Build.PL' --- Build.PL 2012-05-29 12:16:32 +0000 +++ Build.PL 2012-06-01 08:08:46 +0000 @@ -9,7 +9,6 @@ 'Async::MergePoint' => '0.03', 'CPS' => 0, 'Exporter' => '5.57', - 'Heap' => '0.80', 'IO::Poll' => 0, 'Socket' => '1.95', 'Storable' => 0, === modified file 'lib/IO/Async/Internals/TimeQueue.pm' --- lib/IO/Async/Internals/TimeQueue.pm 2012-05-31 11:53:22 +0000 +++ lib/IO/Async/Internals/TimeQueue.pm 2012-06-01 08:08:46 +0000 @@ -8,22 +8,27 @@ use strict; use warnings; -use base qw( Heap::Fibonacci ); use Carp; -use Heap::Fibonacci; use Time::HiRes qw( time ); -sub next_time -{ - my $self = shift; - - my $top = $self->top; - - return defined $top ? $top->time : undef; +BEGIN { + my @methods = qw( next_time _enqueue cancel _fire ); + if( eval { require Heap::Fibonacci } ) { + unshift our @ISA, "Heap::Fibonacci"; + require Heap::Elem; + no strict 'refs'; + *$_ = \&{"HEAP_$_"} for @methods; + } + else { + no strict 'refs'; + *$_ = \&{"ARRAY_$_"} for "new", @methods; + } } +# High-level methods + sub enqueue { my $self = shift; @@ -35,13 +40,97 @@ defined $params{time} or croak "Expected 'time'"; my $time = $params{time}; + $self->_enqueue( $time, $code ); +} + +sub fire +{ + my $self = shift; + my ( %params ) = @_; + + my $now = exists $params{now} ? $params{now} : time; + $self->_fire( $now ); +} + +# Implementation using a Perl array + +use constant { + TIME => 0, + CODE => 1, +}; + +sub ARRAY_new +{ + my $class = shift; + return bless [], $class; +} + +sub ARRAY_next_time +{ + my $self = shift; + return @$self ? $self->[0]->[TIME] : undef; +} + +sub ARRAY__enqueue +{ + my $self = shift; + my ( $time, $code ) = @_; + + # TODO: This could be more efficient maybe using a binary search insert + @$self = sort { $a->[TIME] <=> $b->[TIME] } @$self, my $elem = [ $time, $code ]; + return $elem; +} + +sub ARRAY_cancel +{ + my $self = shift; + my ( $id ) = @_; + + @$self = grep { $_ != $id } @$self; +} + +sub ARRAY__fire +{ + my $self = shift; + my ( $now ) = @_; + + my $count = 0; + + while( @$self ) { + last if( $self->[0]->[TIME] > $now ); + + my $top = shift @$self; + + $top->[CODE]->(); + $count++; + } + + return $count; +} + +# Implementation using Heap::Fibonacci + +sub HEAP_next_time +{ + my $self = shift; + + my $top = $self->top; + + return defined $top ? $top->time : undef; +} + +sub HEAP__enqueue +{ + my $self = shift; + my ( $time, $code ) = @_; + my $elem = IO::Async::Internals::TimeQueue::Elem->new( $time, $code ); $self->add( $elem ); return $elem; } -sub cancel +sub HEAP_cancel { my $self = shift; my ( $id ) = @_; @@ -49,12 +138,10 @@ $self->delete( $id ); } -sub fire +sub HEAP__fire { my $self = shift; - my ( %params ) = @_; - - my $now = exists $params{now} ? $params{now} : time; + my ( $now ) = @_; my $count = 0; @@ -74,7 +161,7 @@ IO::Async::Internals::TimeQueue::Elem; use strict; -use base qw( Heap::Elem ); +our @ISA = qw( Heap::Elem ); sub new {
Now released as part of 0.50 -- Paul Evans