File Coverage

lib/IOMux.pm
Criterion Covered Total %
statement 65 83 78.3
branch 13 40 32.5
condition 3 15 20.0
subroutine 16 20 80.0
pod 10 11 90.9
total 107 169 63.3


line stmt bran cond sub pod time code
1             # Copyrights 2011-2015 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5 8     8   967 use warnings;
  8         17  
  8         210  
6 8     8   39 use strict;
  8         13  
  8         204  
7              
8             package IOMux;
9 8     8   37 use vars '$VERSION';
  8         9  
  8         374  
10             $VERSION = '1.00';
11              
12 8     8   5196 use Log::Report 'iomux';
  8         685958  
  8         59  
13              
14 8     8   2184 use List::Util 'min';
  8         17  
  8         464  
15 8     8   46 use POSIX 'errno_h';
  8         14  
  8         48  
16              
17             $SIG{PIPE} = 'IGNORE'; # pipes are handled in mux
18              
19             use constant
20 8         7854 { LONG_TIMEOUT => 60 # no-one has set a timeout
21 8     8   3770 };
  8         17  
22              
23              
24 7     7 1 3827 sub new(@) {my $class = shift; (bless {}, $class)->init( {@_} ) }
  7         40  
25             sub init($)
26 7     7 0 15 { my ($self, $args) = @_;
27 7         40 $self->{IM_handlers} = {};
28 7         20 $self->{IM_timeouts} = {};
29 7         16 $self;
30             }
31              
32             #-------------
33              
34             #-------------
35              
36             # add() is the main user interface to mux, because from then the
37             # user works with connection objects. Therefore, offer some extra
38             # features here.
39              
40             sub add($)
41 12     12 1 7807 { my ($self, $handler) = @_;
42              
43 12 50 0     84 UNIVERSAL::isa($handler, 'IOMux::Handler')
44             or error __x"attempt to add non handler {pkg}"
45             , pkg => (ref $handler || $handler);
46              
47 12         367 $handler->muxInit($self);
48 12         294 $handler;
49             }
50              
51              
52             sub open(@)
53 4     4 1 2856 { my $self = shift;
54 4 50       36 IOMux::Open->can('new')
55             or error __x"IOMux::Open not loaded";
56 4         15 my $conn = IOMux::Open->new(@_);
57 4 50       24 $self->add($conn) if $conn;
58 4         10 $conn;
59             }
60              
61              
62             sub loop(;$)
63 4     4 1 62 { my($self, $heartbeat) = @_;
64 4         22 $self->{IM_endloop} = 0;
65              
66 4         12 my $handlers = $self->{IM_handlers};
67 4 50       29 keys %$handlers
68             or error __x"there are no handlers for the mux loop";
69              
70             LOOP:
71 4   66     70 while(!$self->{IM_endloop} && keys %$handlers)
72             {
73             # while(my($fileno, $conn) = each %$handlers)
74             # { $conn->read
75             # if $conn->usesSSL && $conn->pending;
76             # }
77              
78 12         29 my $timeout = $self->{IM_next_timeout};
79 12 50       48 my $wait = defined $timeout ? $timeout-time : LONG_TIMEOUT;
80              
81             # For negative values, still give select a chance, to avoid
82             # starvation when timeout handling starts consuming all
83             # processor time.
84 12 50       43 $wait = 0.001 if $wait < 0.001;
85              
86 12 50       47 $self->one_go($wait, $heartbeat)
87             or last LOOP;
88              
89 12         63 $self->_checkTimeouts($timeout);
90             }
91              
92             $_->close
93 4         19 for values %$handlers;
94             }
95              
96              
97 0     0 1 0 sub endLoop($) { $_[0]->{IM_endloop} = $_[1] }
98              
99             #-------------
100              
101 0     0 1 0 sub handlers() {values %{shift->{IM_handlers}}}
  0         0  
102 36     36   91 sub _handlers() {shift->{IM_handlers}}
103              
104              
105             sub handler($;$)
106 45     45 1 94 { my $hs = shift->{IM_handlers};
107 45         65 my $fileno = shift;
108 45 100       216 @_ or return $hs->{$fileno};
109 13 50       110 (defined $_[0]) ? ($hs->{$fileno} = shift) : (delete $hs->{$fileno});
110             }
111              
112              
113             sub remove($)
114 13     13 1 21 { my ($self, $fileno) = @_;
115              
116 13 50       51 my $obj = delete $self->{IM_handlers}{$fileno}
117             or return $self;
118              
119 13         50 $self->fdset($fileno, 0, 1, 1, 1);
120 13         84 $obj->muxRemove;
121              
122 13 50       288 if(my $timeout = delete $self->{IM_timeouts}{$fileno})
123             { delete $self->{IM_next_timeout}
124 0 0       0 if $self->{IM_next_timeout}==$timeout;
125             }
126              
127 13         36 $self;
128             }
129              
130              
131 0     0 1 0 sub fdset($$$$$) {panic}
132              
133              
134             sub changeTimeout($$$)
135 0     0 1 0 { my ($self, $fileno, $old, $when) = @_;
136 0 0       0 return if $old==$when;
137              
138 0         0 my $next = $self->{IM_next_timeout};
139 0 0       0 if($old)
140             { # next timeout will be recalculated max once per loop
141 0         0 delete $self->{IM_timeouts}{$fileno};
142 0 0 0     0 $self->{IM_next_timeout} = $next = undef if $next && $next==$old;
143             }
144              
145 0 0       0 if($when)
146 0 0 0     0 { $self->{IM_next_timeout} = $when if !$next || $next > $when;
147 0         0 $self->{IM_timeouts}{$fileno} = $when;
148             }
149             }
150              
151             # handle all timeouts which have expired either during the select
152             # or during the processing of flags.
153             sub _checkTimeouts($)
154 12     12   23 { my ($self, $next) = @_;
155              
156 12         37 my $now = time;
157 12 50 33     51 if($next && $now < $next)
158             { # Even when next is cancelled, none can have expired.
159             # However, a new timeout may have arrived which may expire immediately.
160 0 0       0 return $next if $self->{IM_next_timeout};
161             }
162              
163 12         144 my $timo = $self->{IM_timeouts};
164 12         27 my $hnd = $self->{IM_handlers};
165 12         66 while(my ($fileno, $when) = each %$timo)
166 0 0       0 { $when <= $now or next;
167 0         0 $hnd->{$fileno}->muxTimeout($self);
168 0         0 delete $timo->{$fileno};
169             }
170              
171 12         125 $self->{IM_next_timeout} = min values %$timo;
172             }
173              
174             1;
175              
176             __END__