ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Coro/Event/Handle.pm
Revision: 1.41
Committed: Sat Nov 4 01:31:57 2006 UTC (19 years, 11 months ago) by root
Branch: MAIN
Changes since 1.40: +22 -8 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     Coro::Handle - non-blocking io with a blocking interface.
4    
5     =head1 SYNOPSIS
6    
7     use Coro::Handle;
8    
9     =head1 DESCRIPTION
10    
11 root 1.41 This module implements IO-handles in a coroutine-compatible way, that is,
12 root 1.1 other coroutines can run while reads or writes block on the handle. It
13     does NOT inherit from IO::Handle but uses tied objects.
14    
15     =over 4
16    
17     =cut
18    
19     package Coro::Handle;
20    
21 pcg 1.23 BEGIN { eval { require warnings } && warnings->unimport ("uninitialized") }
22 root 1.9
23 root 1.1 use Errno ();
24     use base 'Exporter';
25    
26 root 1.39 $VERSION = 1.9;
27 root 1.1
28     @EXPORT = qw(unblock);
29    
30     =item $fh = new_from_fh Coro::Handle $fhandle [, arg => value...]
31    
32     Create a new non-blocking io-handle using the given
33     perl-filehandle. Returns undef if no fhandle is given. The only other
34     supported argument is "timeout", which sets a timeout for each operation.
35    
36     =cut
37    
38     sub new_from_fh {
39     my $class = shift;
40     my $fh = shift or return;
41     my $self = do { local *Coro::Handle };
42    
43     my ($package, $filename, $line) = caller;
44     $filename =~ s/^.*[\/\\]//;
45    
46     tie $self, Coro::Handle::FH, fh => $fh, desc => "$filename:$line", @_;
47    
48 root 1.16 my $_fh = select bless \$self, ref $class ? ref $class : $class; $| = 1; select $_fh;
49 root 1.1 }
50    
51     =item $fh = unblock $fh
52    
53     This is a convinience function that just calls C<new_from_fh> on the given
54     filehandle. Use it to replace a normal perl filehandle by a non-blocking
55     equivalent.
56    
57     =cut
58    
59     sub unblock($) {
60     new_from_fh Coro::Handle $_[0];
61     }
62    
63     =item $fh->writable, $fh->readable
64    
65     Wait until the filehandle is readable or writable (and return true) or
66     until an error condition happens (and return false).
67    
68     =cut
69    
70 root 1.5 sub readable { Coro::Handle::FH::readable(tied ${$_[0]}) }
71     sub writable { Coro::Handle::FH::writable(tied ${$_[0]}) }
72 root 1.1
73     =item $fh->readline([$terminator])
74    
75     Like the builtin of the same name, but allows you to specify the input
76     record separator in a coroutine-safe manner (i.e. not using a global
77     variable).
78    
79     =cut
80    
81     sub readline { tied(${+shift})->READLINE(@_) }
82    
83     =item $fh->autoflush([...])
84    
85     Always returns true, arguments are being ignored (exists for compatibility
86     only). Might change in the future.
87    
88     =cut
89    
90     sub autoflush { !0 }
91    
92 root 1.9 =item $fh->fileno, $fh->close,
93     $fh->read, $fh->sysread, $fh->syswrite,
94     $fh->print, $fh->printf
95    
96     Work like their function equivalents (except read, which works like
97     sysread. You should not use the read function with Coro::Handles, it will
98     work but it's not efficient).
99 root 1.1
100     =cut
101    
102 root 1.9 sub read { Coro::Handle::FH::READ (tied ${$_[0]}, $_[1], $_[2], $_[3]) }
103     sub sysread { Coro::Handle::FH::READ (tied ${$_[0]}, $_[1], $_[2], $_[3]) }
104     sub syswrite { Coro::Handle::FH::WRITE (tied ${$_[0]}, $_[1], $_[2], $_[3]) }
105     sub print { Coro::Handle::FH::WRITE (tied ${+shift}, join "", @_) }
106     sub printf { Coro::Handle::FH::PRINTF(tied ${+shift}, @_) }
107     sub fileno { Coro::Handle::FH::FILENO(tied ${$_[0]}) }
108     sub close { Coro::Handle::FH::CLOSE (tied ${$_[0]}) }
109 root 1.1
110     =item $fh->timeout([...])
111    
112     The optional agrument sets the new timeout (in seconds) for this
113     handle. Returns the current (new) value.
114    
115     C<0> is a valid timeout, use C<undef> to disable the timeout.
116    
117     =cut
118    
119     sub timeout {
120 root 1.4 my $self = tied(${$_[0]});
121 root 1.6 if (@_ > 1) {
122     $self->[2] = $_[1];
123     $self->[5]->timeout($_[1]) if $self->[5];
124     $self->[6]->timeout($_[1]) if $self->[6];
125 root 1.1 }
126 root 1.6 $self->[2];
127 root 1.4 }
128    
129     =item $fh->fh
130    
131     Returns the "real" (non-blocking) filehandle. Use this if you want to
132     do operations on the file handle you cannot do using the Coro::Handle
133     interface.
134    
135 root 1.11 =item $fh->rbuf
136 root 1.10
137 root 1.13 Returns the current contents of the read buffer (this is an lvalue, so you
138 root 1.11 can change the read buffer if you like).
139    
140 root 1.13 You can use this function to implement your own optimized reader when neither
141 root 1.11 readline nor sysread are viable candidates, like this:
142    
143     # first get the _real_ non-blocking filehandle
144 root 1.15 # and fetch a reference to the read buffer
145 root 1.11 my $nb_fh = $fh->fh;
146 root 1.15 my $buf = \$fh->rbuf;
147 root 1.11
148     for(;;) {
149     # now use buffer contents, modifying
150     # if necessary to reflect the removed data
151    
152 root 1.15 last if $$buf ne ""; # we have leftover data
153 root 1.11
154     # read another buffer full of data
155     $fh->readable or die "end of file";
156 root 1.15 sysread $nb_fh, $$buf, 8192;
157 root 1.11 }
158 root 1.10
159 root 1.4 =cut
160    
161     sub fh {
162 root 1.10 (tied ${$_[0]})->[0];
163     }
164    
165 root 1.11 sub rbuf : lvalue {
166 root 1.41 (tied ${$_[0]})->[3];
167     }
168    
169     sub AUTOLOAD {
170     my $self = tied ${$_[0]};
171    
172     (my $func = $AUTOLOAD) =~ s/^(.*):://;
173    
174     my $forward = UNIVERSAL::can $self->[7], $func;
175    
176     $forward or
177     die "Can't locate object method \"$func\" via package \"" . (ref $self) . "\"";
178    
179     goto &$forward;
180 root 1.1 }
181    
182     package Coro::Handle::FH;
183    
184 pcg 1.23 BEGIN { eval { require warnings } && warnings->unimport ("uninitialized") }
185 root 1.9
186 root 1.1 use Fcntl ();
187     use Errno ();
188     use Carp 'croak';
189    
190     use Coro::Event;
191     use Event::Watcher qw(R W E);
192    
193 root 1.6 # formerly a hash, but we are speed-critical, so try
194     # to be faster even if it hurts.
195     #
196     # 0 FH
197     # 1 desc
198     # 2 timeout
199     # 3 rb
200 root 1.10 # 4 wb # unused
201 root 1.6 # 5 rw
202     # 6 ww
203 root 1.41 # 7 forward class
204 root 1.6
205 root 1.1 sub TIEHANDLE {
206 root 1.41 my ($class, %arg) = @_;
207 root 1.1
208 root 1.6 my $self = bless [], $class;
209 root 1.41 $self->[0] = $arg{fh};
210     $self->[1] = $arg{desc};
211     $self->[2] = $arg{timeout};
212 root 1.6 $self->[3] = "";
213     $self->[4] = "";
214 root 1.41 $self->[7] = $arg{forward_class};
215 root 1.1
216 root 1.6 fcntl $self->[0], &Fcntl::F_SETFL, &Fcntl::O_NONBLOCK
217 root 1.1 or croak "fcntl(O_NONBLOCK): $!";
218    
219 root 1.41 $self
220 root 1.1 }
221    
222 root 1.6 sub cleanup {
223     $_[0][3] = "";
224 root 1.7 ($_[0][5])->cancel if defined $_[0][5]; $_[0][5] = undef;
225 root 1.6
226     $_[0][4] = "";
227 root 1.7 ($_[0][6])->cancel if defined $_[0][6]; $_[0][6] = undef;
228 root 1.6 }
229    
230 root 1.1 sub OPEN {
231 root 1.6 &cleanup;
232 root 1.1 my $self = shift;
233 root 1.6 my $r = @_ == 2 ? open $self->[0], $_[0], $_[1]
234     : open $self->[0], $_[0], $_[1], $_[2];
235 root 1.1 if ($r) {
236 root 1.6 fcntl $self->[0], &Fcntl::F_SETFL, &Fcntl::O_NONBLOCK
237 root 1.1 or croak "fcntl(O_NONBLOCK): $!";
238     }
239     $r;
240 root 1.9 }
241    
242     sub PRINT {
243 root 1.12 WRITE(shift, join "", @_);
244 root 1.9 }
245    
246     sub PRINTF {
247     WRITE(shift, sprintf(shift,@_));
248     }
249    
250     sub GETC {
251     my $buf;
252     READ($_[0], $buf, 1);
253     $buf;
254     }
255    
256     sub BINMODE {
257     binmode $_[0][0];
258     }
259    
260     sub TELL {
261     use Carp (); Carp::croak("Coro::Handle's don't support tell()");
262     }
263    
264     sub SEEK {
265     use Carp (); Carp::croak("Coro::Handle's don't support seek()");
266     }
267    
268     sub EOF {
269     use Carp (); Carp::croak("Coro::Handle's don't support eof()");
270 root 1.1 }
271    
272     sub CLOSE {
273 root 1.6 &cleanup;
274     close $_[0][0];
275 root 1.1 }
276    
277 root 1.6 sub DESTROY {
278     &cleanup;
279 root 1.1 }
280    
281 root 1.6 sub FILENO {
282     fileno $_[0][0];
283 root 1.1 }
284    
285 root 1.8 # seems to be called for stringification (how weird), at least
286     # when DumpValue::dumpValue is used to print this.
287     sub FETCH {
288     "$_[0]<$_[0][1]>";
289     }
290    
291 root 1.1 sub readable {
292 root 1.6 ($_[0][5] ||= Coro::Event->io(
293     fd => $_[0][0],
294     desc => "$_[0][1] R",
295     timeout => $_[0][2],
296 root 1.1 poll => R+E,
297 root 1.3 ))->next->{Coro::Event}[5] & R;
298 root 1.1 }
299    
300 root 1.6 sub writable {
301     ($_[0][6] ||= Coro::Event->io(
302     fd => $_[0][0],
303     desc => "$_[0][1] W",
304     timeout => $_[0][2],
305     poll => W+E,
306     ))->next->{Coro::Event}[5] & W;
307     }
308    
309 root 1.1 sub WRITE {
310     my $len = defined $_[2] ? $_[2] : length $_[1];
311     my $ofs = $_[3];
312     my $res = 0;
313    
314     while() {
315 root 1.6 my $r = syswrite $_[0][0], $_[1], $len, $ofs;
316 root 1.1 if (defined $r) {
317     $len -= $r;
318     $ofs += $r;
319     $res += $r;
320     last unless $len;
321     } elsif ($! != Errno::EAGAIN) {
322     last;
323     }
324 root 1.6 last unless &writable;
325 root 1.1 }
326    
327     return $res;
328     }
329    
330     sub READ {
331     my $len = $_[2];
332     my $ofs = $_[3];
333     my $res = 0;
334    
335     # first deplete the read buffer
336 root 1.14 if (length $_[0][3]) {
337 root 1.6 my $l = length $_[0][3];
338 root 1.1 if ($l <= $len) {
339 root 1.14 substr($_[1], $ofs) = $_[0][3]; $_[0][3] = "";
340 root 1.1 $len -= $l;
341 root 1.19 $ofs += $l;
342 root 1.1 $res += $l;
343     return $res unless $len;
344     } else {
345 root 1.6 substr($_[1], $ofs) = substr($_[0][3], 0, $len);
346     substr($_[0][3], 0, $len) = "";
347 root 1.1 return $len;
348     }
349     }
350    
351     while() {
352 root 1.6 my $r = sysread $_[0][0], $_[1], $len, $ofs;
353 root 1.1 if (defined $r) {
354     $len -= $r;
355     $ofs += $r;
356     $res += $r;
357     last unless $len && $r;
358     } elsif ($! != Errno::EAGAIN) {
359     last;
360     }
361 root 1.6 last unless &readable;
362 root 1.1 }
363    
364     return $res;
365     }
366    
367     sub READLINE {
368 root 1.6 my $irs = @_ > 1 ? $_[1] : $/;
369 root 1.1
370     while() {
371 root 1.6 my $pos = index $_[0][3], $irs;
372 root 1.1 if ($pos >= 0) {
373     $pos += length $irs;
374 root 1.6 my $res = substr $_[0][3], 0, $pos;
375     substr ($_[0][3], 0, $pos) = "";
376 root 1.1 return $res;
377     }
378 root 1.6
379     my $r = sysread $_[0][0], $_[0][3], 8192, length $_[0][3];
380 root 1.1 if (defined $r) {
381     return undef unless $r;
382 root 1.6 } elsif ($! != Errno::EAGAIN || !&readable) {
383 root 1.1 return undef;
384     }
385     }
386     }
387    
388     1;
389    
390 root 1.40 =back
391    
392 root 1.1 =head1 BUGS
393    
394     - Perl's IO-Handle model is THE bug.
395    
396     =head1 AUTHOR
397    
398 root 1.31 Marc Lehmann <schmorp@schmorp.de>
399 root 1.29 http://home.schmorp.de/
400 root 1.1
401     =cut
402