File Coverage

blib/lib/IO/Async/Internals/TimeQueue.pm
Criterion Covered Total %
statement 59 95 62.1
branch 11 16 68.7
condition 3 6 50.0
subroutine 15 23 65.2
pod 0 11 0.0
total 88 151 58.2


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2006-2024 -- leonerd@leonerd.org.uk
5              
6             package # hide from CPAN
7             IO::Async::Internals::TimeQueue 0.802;
8              
9 77     77   232860 use v5.14;
  77         383  
10 77     77   478 use warnings;
  77         212  
  77         8209  
11              
12 77     77   818 use Carp;
  77         183  
  77         13675  
13              
14 77     77   581 use Time::HiRes qw( time );
  77         339  
  77         1864  
15              
16             BEGIN {
17 77     77   382 my @methods = qw( next_time _enqueue cancel _fire );
18 77 50       380 if( eval { require Heap::Fibonacci } ) {
  77         9741  
19 0         0 unshift our @ISA, "Heap::Fibonacci";
20 0         0 require Heap::Elem;
21 77     77   14901 no strict 'refs';
  77         175  
  77         9022  
22 0         0 *$_ = \&{"HEAP_$_"} for @methods;
  0         0  
23             }
24             else {
25 77     77   558 no strict 'refs';
  77         302  
  77         6417  
26 77         323 *$_ = \&{"ARRAY_$_"} for "new", @methods;
  385         73446  
27             }
28             }
29              
30             # High-level methods
31              
32             sub enqueue
33             {
34 705     705 0 3440 my $self = shift;
35 705         9000 my ( %params ) = @_;
36              
37 705         24438 my $code = delete $params{code};
38 705 100       2809 ref $code or croak "Expected 'code' to be a reference";
39              
40 703 100       2428 defined $params{time} or croak "Expected 'time'";
41 702         1230 my $time = $params{time};
42              
43 702         2992 $self->_enqueue( $time, $code );
44             }
45              
46             sub fire
47             {
48 1383     1383 0 3590 my $self = shift;
49 1383         3650 my ( %params ) = @_;
50              
51 1383 100       6927 my $now = exists $params{now} ? $params{now} : time;
52 1383         5005 $self->_fire( $now );
53             }
54              
55             # Implementation using a Perl array
56              
57             use constant {
58 77         75411 TIME => 0,
59             CODE => 1,
60 77     77   626 };
  77         186  
61              
62             sub ARRAY_new
63             {
64 77     77 0 219790 my $class = shift;
65 77         1004 return bless [], $class;
66             }
67              
68             sub ARRAY_next_time
69             {
70 1367     1367 0 14298 my $self = shift;
71 1367 100       5398 return @$self ? $self->[0]->[TIME] : undef;
72             }
73              
74             sub ARRAY__enqueue
75             {
76 702     702 0 3703 my $self = shift;
77 702         1625 my ( $time, $code ) = @_;
78              
79             # TODO: This could be more efficient maybe using a binary search
80 702         1697 my $idx = 0;
81 702   100     7691 $idx++ while $idx < @$self and $self->[$idx][TIME] <= $time;
82 702         3199 splice @$self, $idx, 0, ( my $elem = [ $time, $code ]);
83              
84 702         3788 return $elem;
85             }
86              
87             sub ARRAY_cancel
88             {
89 524     524 0 1067 my $self = shift;
90 524         1153 my ( $id ) = @_;
91              
92 524         1629 @$self = grep { $_ != $id } @$self;
  572         5258  
93             }
94              
95             sub ARRAY__fire
96             {
97 1383     1383 0 2430 my $self = shift;
98 1383         3195 my ( $now ) = @_;
99              
100 1383         2534 my $count = 0;
101              
102 1383         4110 while( @$self ) {
103 1507 100       5531 last if( $self->[0]->[TIME] > $now );
104              
105 177         343 my $top = shift @$self;
106              
107 177         644 $top->[CODE]->();
108 175         843 $count++;
109             }
110              
111 1381         4503 return $count;
112             }
113              
114             # Implementation using Heap::Fibonacci
115              
116             sub HEAP_next_time
117             {
118 0     0 0   my $self = shift;
119              
120 0           my $top = $self->top;
121              
122 0 0         return defined $top ? $top->time : undef;
123             }
124              
125             sub HEAP__enqueue
126             {
127 0     0 0   my $self = shift;
128 0           my ( $time, $code ) = @_;
129              
130 0           my $elem = IO::Async::Internals::TimeQueue::Elem->new( $time, $code );
131 0           $self->add( $elem );
132              
133 0           return $elem;
134             }
135              
136             sub HEAP_cancel
137             {
138 0     0 0   my $self = shift;
139 0           my ( $id ) = @_;
140              
141 0           $self->delete( $id );
142             }
143              
144             sub HEAP__fire
145             {
146 0     0 0   my $self = shift;
147 0           my ( $now ) = @_;
148              
149 0           my $count = 0;
150              
151 0           while( defined( my $top = $self->top ) ) {
152 0 0         last if( $top->time > $now );
153              
154 0           $self->extract_top;
155              
156 0           $top->code->();
157 0           $count++;
158             }
159              
160 0           return $count;
161             }
162              
163             package # hide from CPAN
164             IO::Async::Internals::TimeQueue::Elem;
165              
166             our @ISA = qw( Heap::Elem );
167              
168             sub new
169             {
170 0     0     my $self = shift;
171 0   0       my $class = ref $self || $self;
172              
173 0           my ( $time, $code ) = @_;
174              
175 0           my $new = $class->SUPER::new(
176             time => $time,
177             code => $code,
178             );
179              
180 0           return $new;
181             }
182              
183             sub time
184             {
185 0     0     my $self = shift;
186 0           return $self->val->{time};
187             }
188              
189             sub code
190             {
191 0     0     my $self = shift;
192 0           return $self->val->{code};
193             }
194              
195             # This only uses methods so is transparent to HASH or ARRAY
196             sub cmp
197             {
198 0     0     my $self = shift;
199 0           my $other = shift;
200              
201 0           $self->time <=> $other->time;
202             }
203              
204             0x55AA;