root/trunk/extlib/IO/SessionData.pm @ 3531

Revision 3531, 6.6 kB (checked in by fumiakiy, 9 months ago)

Merged sockfish to trunk. "svn merge -r3114:3527 http://code.sixapart.com/svn/movabletype/branches/sockfish/ ."

Line 
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
10package IO::SessionData;
11
12use strict;
13use Carp;
14use IO::SessionSet;
15use vars '$VERSION';
16$VERSION = 1.02;
17
18use constant BUFSIZE => 3000;
19
20BEGIN {
21    my @names = qw(EWOULDBLOCK EAGAIN EINPROGRESS);
22    my %WOULDBLOCK =
23        (eval {require Errno}
24            ? map {
25                Errno->can($_)
26                    ? (Errno->can($_)->() => 1)
27                    : (),
28                } @names
29            : ()
30        ),
31        (eval {require POSIX}
32            ? map {
33                eval { POSIX->can($_)->() }
34                    ? (POSIX->can($_)->() => 1)
35                    : ()
36                } @names
37            : ()
38        );
39
40    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.
46sub new {
47    my $pack = shift;
48    my ($sset,$handle,$writeonly) = @_;
49    # make the handle nonblocking (but check for 'blocking' method first)
50    # thanks to Jos Clijmans <jos.clijmans@recyfin.be>
51    $handle->blocking(0) if $handle->can('blocking');
52    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    $self->readable(1) unless $writeonly;
62    return $self;
63}
64
65# Object method: handle()
66# Return the IO::Handle object corresponding to this IO::SessionData
67sub handle {
68    return shift->{handle};
69}
70
71# Object method: sessions()
72# Return the IO::SessionSet controlling this object.
73sub sessions {
74    return shift->{sset};
75}
76
77# Object method: pending()
78# returns number of bytes pending in the out buffer
79sub pending {
80    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.
86sub write_limit {
87    my $self = shift;
88    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.
95sub set_choke {
96    my $self = shift;
97    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.
106sub write {
107    my $self = shift;
108    return unless my $handle = $self->handle; # no handle
109    return unless defined $self->{outbuffer}; # no buffer for queued data
110
111    $self->{outbuffer} .= $_[0] if defined $_[0];
112
113    my $rc;
114    if ($self->pending) { # data in the out buffer to write
115        local $SIG{PIPE}='IGNORE';
116        # added length() to make it work on Mac. Thanks to Robin Fuller <rfuller@broadjump.com>
117        $rc = syswrite($handle,$self->{outbuffer},length($self->{outbuffer}));
118
119        # able to write, so truncate out buffer apropriately
120        if ($rc) {
121            substr($self->{outbuffer},0,$rc) = '';
122        }
123        elsif (WOULDBLOCK($!)) {  # this is OK
124            $rc = '0E0';
125        }
126        else { # some sort of write error, such as a PIPE error
127            return $self->bail_out($!);
128        }
129    }
130    else {
131        $rc = '0E0';   # nothing to do, but no error either
132    }
133
134    $self->adjust_state;
135
136    # Result code is the number of bytes successfully transmitted
137    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.
143sub read {
144    my $self = shift;
145    return unless my $handle = $self->handle;
146    my $rc = sysread($handle,$_[0],$_[1],$_[2]||0);
147    return $rc if defined $rc;
148    return '0E0' if WOULDBLOCK($!);
149    return;
150}
151
152# Object method: close()
153# Close the session and remove it from the monitored list.
154sub close {
155    my $self = shift;
156    unless ($self->pending) {
157        $self->sessions->delete($self);
158        CORE::close($self->handle);
159    }
160    else {
161        $self->readable(0);
162        $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
169sub adjust_state {
170    my $self = shift;
171
172    # make writable if there's anything in the out buffer
173    $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    $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    $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.
187sub choke {
188    my $self = shift;
189    my $do_choke = shift;
190    return if $self->{choked} == $do_choke;  # no change in state
191    if (ref $self->set_choke eq 'CODE') {
192        $self->set_choke->($self,$do_choke);
193    }
194    else {
195        $self->readable(!$do_choke);
196    }
197    $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.
202sub readable {
203    my $self = shift;
204    my $is_active = shift;
205    return if $self->{writeonly};
206    $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.
211sub writable {
212    my $self = shift;
213    my $is_active = shift;
214    $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.
221sub bail_out {
222    my $self = shift;
223    my $errcode = shift;           # save errorno
224    delete $self->{outbuffer};     # drop buffered data
225    $self->close;
226    $! = $errcode;                 # restore errno
227    return;
228}
229
2301;
Note: See TracBrowser for help on using the browser.