File Coverage

blib/lib/IO/SessionData.pm
Criterion Covered Total %
statement 34 86 39.5
branch 10 46 21.7
condition 1 5 20.0
subroutine 12 21 57.1
pod 0 15 0.0
total 57 173 32.9


line stmt bran cond sub pod time code
1             # ======================================================================
2             #
3             # Copyright (C) 2000 Lincoln D. Stein
4             # Slightly modified by Paul Kulchenko to work on multiple platforms
5             # Formatting changed to match the layout layed out in Perl Best Practices
6             # (by Damian Conway) by Martin Kutter in 2008
7             #
8             # ======================================================================
9              
10             package IO::SessionData;
11              
12 2     2   3673 use strict;
  2         6  
  2         79  
13 2     2   12 use Carp;
  2         5  
  2         151  
14 2     2   4182 use IO::SessionSet;
  2         5  
  2         57  
15 2     2   13 use vars '$VERSION';
  2         5  
  2         130  
16             $VERSION = 1.03;
17              
18 2     2   306 use constant BUFSIZE => 3000;
  2         5  
  2         473  
19              
20             BEGIN {
21 2     2   5 my @names = qw(EWOULDBLOCK EAGAIN EINPROGRESS);
22 2         23 my %WOULDBLOCK =
23 6 50       94 (eval {require Errno}
24             ? map {
25 2         2688 Errno->can($_)
26             ? (Errno->can($_)->() => 1)
27             : (),
28             } @names
29             : ()
30             ),
31             (eval {require POSIX}
32             ? map {
33 2 50 33     4 POSIX->can($_) && eval { POSIX->can($_)->() }
  6 50       21327  
    50          
34             ? (POSIX->can($_)->() => 1)
35             : ()
36             } @names
37             : ()
38             );
39              
40 0     0 0 0 sub WOULDBLOCK { $WOULDBLOCK{$_[0]+0} }
41             }
42              
43             # Class method: new()
44             # Create a new IO::SessionData object. Intended to be called from within
45             # IO::SessionSet, not directly.
46             sub new {
47 1     1 0 1899 my $pack = shift;
48 1         4 my ($sset,$handle,$writeonly) = @_;
49             # make the handle nonblocking (but check for 'blocking' method first)
50             # thanks to Jos Clijmans
51 1 50       15 $handle->blocking(0) if $handle->can('blocking');
52 1         11 my $self = bless {
53             outbuffer => '',
54             sset => $sset,
55             handle => $handle,
56             write_limit => BUFSIZE,
57             writeonly => $writeonly,
58             choker => undef,
59             choked => 0,
60             },$pack;
61 1 50       4 $self->readable(1) unless $writeonly;
62 1         10 return $self;
63             }
64              
65             # Object method: handle()
66             # Return the IO::Handle object corresponding to this IO::SessionData
67             sub handle {
68 1     1 0 8 return shift->{handle};
69             }
70              
71             # Object method: sessions()
72             # Return the IO::SessionSet controlling this object.
73             sub sessions {
74 1     1 0 6 return shift->{sset};
75             }
76              
77             # Object method: pending()
78             # returns number of bytes pending in the out buffer
79             sub pending {
80 1     1 0 7 return length shift->{outbuffer};
81             }
82              
83             # Object method: write_limit([$bufsize])
84             # Get or set the limit on the size of the write buffer.
85             # Write buffer will grow to this size plus whatever extra you write to it.
86             sub write_limit {
87 2     2 0 5 my $self = shift;
88 2 100       18 return defined $_[0]
89             ? $self->{write_limit} = $_[0]
90             : $self->{write_limit};
91             }
92              
93             # set a callback to be called when the contents of the write buffer becomes larger
94             # than the set limit.
95             sub set_choke {
96 2     2 0 5 my $self = shift;
97 2 100       13 return defined $_[0]
98             ? $self->{choker} = $_[0]
99             : $self->{choker};
100             }
101              
102             # Object method: write($scalar)
103             # $obj->write([$data]) -- append data to buffer and try to write to handle
104             # Returns number of bytes written, or 0E0 (zero but true) if data queued but not
105             # written. On other errors, returns undef.
106             sub write {
107 0     0 0   my $self = shift;
108 0 0         return unless my $handle = $self->handle; # no handle
109 0 0         return unless defined $self->{outbuffer}; # no buffer for queued data
110              
111 0 0         $self->{outbuffer} .= $_[0] if defined $_[0];
112              
113 0           my $rc;
114 0 0         if ($self->pending) { # data in the out buffer to write
115 0           local $SIG{PIPE}='IGNORE';
116             # added length() to make it work on Mac. Thanks to Robin Fuller
117 0           $rc = syswrite($handle,$self->{outbuffer},length($self->{outbuffer}));
118              
119             # able to write, so truncate out buffer apropriately
120 0 0         if ($rc) {
    0          
121 0           substr($self->{outbuffer},0,$rc) = '';
122             }
123             elsif (WOULDBLOCK($!)) { # this is OK
124 0           $rc = '0E0';
125             }
126             else { # some sort of write error, such as a PIPE error
127 0           return $self->bail_out($!);
128             }
129             }
130             else {
131 0           $rc = '0E0'; # nothing to do, but no error either
132             }
133              
134 0           $self->adjust_state;
135              
136             # Result code is the number of bytes successfully transmitted
137 0           return $rc;
138             }
139              
140             # Object method: read($scalar,$length [,$offset])
141             # Just like sysread(), but returns the number of bytes read on success,
142             # 0EO ("0 but true") if the read would block, and undef on EOF and other failures.
143             sub read {
144 0     0 0   my $self = shift;
145 0 0         return unless my $handle = $self->handle;
146 0   0       my $rc = sysread($handle,$_[0],$_[1],$_[2]||0);
147 0 0         return $rc if defined $rc;
148 0 0         return '0E0' if WOULDBLOCK($!);
149 0           return;
150             }
151              
152             # Object method: close()
153             # Close the session and remove it from the monitored list.
154             sub close {
155 0     0 0   my $self = shift;
156 0 0         unless ($self->pending) {
157 0           $self->sessions->delete($self);
158 0           CORE::close($self->handle);
159             }
160             else {
161 0           $self->readable(0);
162 0           $self->{closing}++; # delayed close
163             }
164             }
165              
166             # Object method: adjust_state()
167             # Called periodically from within write() to control the
168             # status of the handle on the IO::SessionSet's IO::Select sets
169             sub adjust_state {
170 0     0 0   my $self = shift;
171              
172             # make writable if there's anything in the out buffer
173 0           $self->writable($self->pending > 0);
174              
175             # make readable if there's no write limit, or the amount in the out
176             # buffer is less than the write limit.
177 0 0         $self->choke($self->write_limit <= $self->pending) if $self->write_limit;
178              
179             # Try to close down the session if it is flagged
180             # as in the closing state.
181 0 0         $self->close if $self->{closing};
182             }
183              
184             # choke gets called when the contents of the write buffer are larger
185             # than the limit. The default action is to inactivate the session for further
186             # reading until the situation is cleared.
187             sub choke {
188 0     0 0   my $self = shift;
189 0           my $do_choke = shift;
190 0 0         return if $self->{choked} == $do_choke; # no change in state
191 0 0         if (ref $self->set_choke eq 'CODE') {
192 0           $self->set_choke->($self,$do_choke);
193             }
194             else {
195 0           $self->readable(!$do_choke);
196             }
197 0           $self->{choked} = $do_choke;
198             }
199              
200             # Object method: readable($flag)
201             # Flag the associated IO::SessionSet that we want to do reading on the handle.
202             sub readable {
203 0     0 0   my $self = shift;
204 0           my $is_active = shift;
205 0 0         return if $self->{writeonly};
206 0           $self->sessions->activate($self,'read',$is_active);
207             }
208              
209             # Object method: writable($flag)
210             # Flag the associated IO::SessionSet that we want to do writing on the handle.
211             sub writable {
212 0     0 0   my $self = shift;
213 0           my $is_active = shift;
214 0           $self->sessions->activate($self,'write',$is_active);
215             }
216              
217             # Object method: bail_out([$errcode])
218             # Called when an error is encountered during writing (such as a PIPE).
219             # Default behavior is to flush all buffered outgoing data and to close
220             # the handle.
221             sub bail_out {
222 0     0 0   my $self = shift;
223 0           my $errcode = shift; # save errorno
224 0           delete $self->{outbuffer}; # drop buffered data
225 0           $self->close;
226 0           $! = $errcode; # restore errno
227 0           return;
228             }
229              
230             1;