File Coverage

blib/lib/IO/Lambda/Poll.pm
Criterion Covered Total %
statement 81 94 86.1
branch 33 54 61.1
condition 7 18 38.8
subroutine 15 16 93.7
pod 3 8 37.5
total 139 190 73.1


line stmt bran cond sub pod time code
1             # $Id: Poll.pm,v 1.7 2009/11/30 13:25:06 dk Exp $
2             package IO::Lambda::Poll;
3 2         317 use vars qw(
4             @ISA @EXPORT_OK %EXPORT_TAGS
5             $DEBUG @RECORDS @TIMER $TIMER_ACTIVE $MASTER
6 2     2   962 );
  2         2  
7              
8             $DEBUG = $IO::Lambda::DEBUG{poll} || 0;
9             @ISA = qw(Exporter);
10             @EXPORT_OK = qw(poll_event poll_cancel poller);
11             %EXPORT_TAGS = ( all => \@EXPORT_OK);
12              
13 2     2   9 use strict;
  2         4  
  2         50  
14 2     2   10 use warnings;
  2         2  
  2         69  
15 2     2   10 use Time::HiRes qw(time);
  2         18  
  2         12  
16 2     2   188 use IO::Lambda qw(:all :dev set_frame get_frame);
  2         2  
  2         2919  
17              
18             $MASTER = bless {}, __PACKAGE__;
19              
20             # register yield handler
21             IO::Lambda::add_loop($MASTER);
22             END {
23 2     2   1366 @RECORDS = ();
24 2         27 IO::Lambda::remove_loop($MASTER);
25             };
26              
27             # There'll also be a single timer as we need timeouts
28             $TIMER[WATCH_OBJ] = bless {}, "IO::Lambda::Poll::Timer";
29             sub IO::Lambda::Poll::Timer::io_handler
30             {
31 2 50   2   17 warn "poll.timer < expired\n" if $DEBUG;
32 2         19 $TIMER_ACTIVE = 0;
33             }
34              
35 18     18 0 120 sub empty { 0 == @RECORDS }
36              
37             sub remove
38             {
39 3     3 0 6 my $lambda = $_[1];
40 3         6 my $n = @RECORDS;
41 3         7 @RECORDS = grep { $_-> {this} ne $lambda } @RECORDS;
  2         12  
42 3 100       15 return if $n == @RECORDS;
43 1 50       5 warn "poll.remove $lambda\n" if $DEBUG;
44 1         4 reset_timer();
45             }
46              
47             sub yield
48             {
49 16 50   16 0 59 warn "poll.yield\n" if $DEBUG > 1;
50 16         72 my $time = time;
51              
52 16         29 my @new;
53 16         75 my @frame = get_frame;
54 16         55 for my $rec ( @RECORDS) {
55             my ( $ok, @result) = $rec-> {poller}-> (
56             defined($rec->{deadline}) && $rec->{deadline} <= $time,
57 16   100     180 @{ $rec-> {param}}
  16         96  
58             );
59 16 100       60 unless ($ok) {
60 10         26 push @new, $rec;
61 10         36 next;
62             }
63 6 50       23 warn "poll.resolve($rec)\n" if $DEBUG;
64 6         20 my $this = $rec-> {this};
65 6         22 $this-> set_frame($rec-> {method}, $rec->{callback}, @{ $rec->{context} });
  6         45  
66 6         45 $this-> callout( $rec-> {callback}, @result);
67 6         37 $this-> resolve( $rec-> {bind});
68             }
69 16         75 set_frame(@frame);
70 16 100       93 return if @RECORDS == @new;
71              
72 6         52 @RECORDS = @new;
73 6         24 reset_timer();
74             }
75              
76             sub reset_timer
77             {
78 14     14 0 26 my ( $expires, $frequency);
79 14         41 for my $rec (@RECORDS) {
80 7         25 my ($f,$d) = @{$rec}{qw(frequency deadline)};
  7         28  
81 7 50 0     38 $frequency = $f if not defined($frequency) or (defined($f) and $frequency > $f);
      33        
82 7 50 0     40 $expires = $d if not defined($expires) or (defined($d) and $expires > $d);
      33        
83             }
84              
85 14 100       49 if ( defined $frequency) {
86 4         17 $frequency += time;
87 4 50       24 if ( defined $expires) {
    0          
88 4 100       19 $expires = $frequency if $expires > $frequency;
89             } elsif ( @RECORDS) {
90 0         0 $expires = $frequency;
91             }
92             }
93              
94 14 100       79 if ( defined $expires) {
    100          
95 4 50       15 if ( $TIMER_ACTIVE) {
96 0 0       0 if ( abs( $expires - $TIMER[WATCH_DEADLINE]) > 0.001) {
97             # restart the active timer
98 0 0       0 warn "poll.timer > restart $expires/$TIMER[WATCH_DEADLINE]\n"
99             if $DEBUG;
100 0         0 $IO::Lambda::LOOP-> remove_event( \@TIMER);
101 0         0 $TIMER[WATCH_DEADLINE] = $expires;
102 0         0 $IO::Lambda::LOOP-> after( \@TIMER);
103             }
104             # else, same timeout, on already active timer - do nothing
105             } else {
106             # resubmit
107 4 50       14 warn "poll.timer > submit $expires\n" if $DEBUG;
108 4         10 $TIMER[WATCH_DEADLINE] = $expires;
109 4         25 $IO::Lambda::LOOP-> after( \@TIMER);
110 4         12 $TIMER_ACTIVE = 1;
111             }
112             } elsif ( $TIMER_ACTIVE) {
113 2 50       12 warn "poll.timer > stop\n" if $DEBUG;
114             # stop timer
115 2         18 $IO::Lambda::LOOP-> remove_event( \@TIMER);
116 2         10 $TIMER_ACTIVE = 0;
117             }
118             }
119              
120             sub poll_event
121             {
122 7     7 1 32 my ( $cb, $method, $poller, $deadline, $frequency, @param ) = @_;
123              
124 7 100 66     59 $deadline += time if defined($deadline) and $deadline < 1_000_000_000;
125            
126 7         30 push @RECORDS, {
127             this => this,
128             bind => this-> bind,
129             method => $method,
130             callback => $cb,
131             context => [ context ],
132             poller => $poller,
133             deadline => $deadline,
134             param => \@param,
135             frequency => $frequency,
136             };
137              
138 7         24 reset_timer;
139 7 50       36 warn "poll.new($RECORDS[-1]) on ", this, "\n" if $DEBUG;
140              
141 7         41 return $RECORDS[-1];
142             }
143              
144             # don't call this, use lambda-> cancel_event( $record->{bind} )
145             sub poll_cancel
146             {
147 0     0 1 0 my $rec = shift;
148 0         0 my $n = @RECORDS;
149 0         0 @RECORDS = grep { $rec != $_ } @RECORDS;
  0         0  
150 0 0       0 return if $n == @RECORDS;
151 0 0       0 warn "poll.cancel($rec)\n" if $DEBUG;
152 0         0 reset_timer;
153             }
154              
155             sub poll_handler
156             {
157 9     9 0 33 my ( $expired, $cb, @opt) = @_;
158 9         44 my @res = $cb->(@opt);
159 9 100       69 return 1,@res if $res[0];
160 6 100       26 return 1,0 if $expired;
161 5         23 return 0;
162             }
163              
164             sub poller(&)
165             {
166 2     2 1 214 my $cb = _subname poller => shift;
167              
168             lambda {
169 5     5   18 my %opt = @_;
170             poll_event(
171             undef, undef, \&poll_handler,
172             exists($opt{timeout}) ? $opt{timeout} : $opt{deadline},
173             $opt{frequency},
174 5 100       46 $cb, %opt
175             );
176             }
177 2         18 }
178              
179             1;
180              
181             __DATA__