summaryrefslogtreecommitdiff
path: root/ext/IO/lib/IO/Select.pm
diff options
context:
space:
mode:
Diffstat (limited to 'ext/IO/lib/IO/Select.pm')
-rw-r--r--ext/IO/lib/IO/Select.pm280
1 files changed, 280 insertions, 0 deletions
diff --git a/ext/IO/lib/IO/Select.pm b/ext/IO/lib/IO/Select.pm
new file mode 100644
index 0000000000..208be0cf53
--- /dev/null
+++ b/ext/IO/lib/IO/Select.pm
@@ -0,0 +1,280 @@
+# IO::Select.pm
+
+package IO::Select;
+
+=head1 NAME
+
+IO::Select - OO interface to the system select call
+
+=head1 SYNOPSYS
+
+ use IO::Select;
+
+ $s = IO::Select->new();
+
+ $s->add(\*STDIN);
+ $s->add($some_handle);
+
+ @ready = $s->can_read($timeout);
+
+ @ready = IO::Select->new(@handles)->read(0);
+
+=head1 DESCRIPTION
+
+The C<IO::Select> package implements an object approach to the system C<select>
+function call. It allows the user to see what IO handles, see L<IO::Handle>,
+are ready for reading, writing or have an error condition pending.
+
+=head1 CONSTRUCTOR
+
+=over 4
+
+=item new ( [ HANDLES ] )
+
+The constructor create a new object and optionally initialises it with a set
+of handles.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item add ( HANDLES )
+
+Add the list of handles to the C<IO::Select> object. It is these values that
+will be returned when an event occurs. C<IO::Select> keeps these values in a
+cache which is indexed by the C<fileno> of the handle, so if more than one
+handle with the same C<fileno> is specified then only the last one is cached.
+
+=item remove ( HANDLES )
+
+Remove all the given handles from the object.
+
+=item can_read ( [ TIMEOUT ] )
+
+Return an array of handles that are ready for reading. C<TIMEOUT> is the maximum
+amount of time to wait before returning an empty list. If C<TIMEOUT> is
+not given then the call will block.
+
+=item can_write ( [ TIMEOUT ] )
+
+Same as C<can_read> except check for handles that can be written to.
+
+=item has_error ( [ TIMEOUT ] )
+
+Same as C<can_read> except check for handles that have an error condition, for
+example EOF.
+
+=item select ( READ, WRITE, ERROR [, TIMEOUT ] )
+
+C<select> is a static method, that is you call it with the package name
+like C<new>. C<READ>, C<WRITE> and C<ERROR> are either C<undef> or
+C<IO::Select> objects. C<TIMEOUT> is optional and has the same effect as
+before.
+
+The result will be an array of 3 elements, each a reference to an array
+which will hold the handles that are ready for reading, writing and have
+error conditions respectively. Upon error an empty array is returned.
+
+=back
+
+=head1 EXAMPLE
+
+Here is a short example which shows how C<IO::Select> could be used
+to write a server which communicates with several sockets while also
+listening for more connections on a listen socket
+
+ use IO::Select;
+ use IO::Socket;
+
+ $lsn = new IO::Socket::INET(Listen => 1, LocalPort => 8080);
+ $sel = new IO::Select( $lsn );
+
+ while(@ready = $sel->can_read) {
+ foreach $fh (@ready) {
+ if($fh == $lsn) {
+ # Create a new socket
+ $new = $lsn->accept;
+ $sel->add($new);
+ }
+ else {
+ # Process socket
+
+ # Maybe we have finished with the socket
+ $sel->remove($fh);
+ $fh->close;
+ }
+ }
+ }
+
+=head1 AUTHOR
+
+Graham Barr <Graham.Barr@tiuk.ti.com>
+
+=head1 REVISION
+
+$Revision: 1.2 $
+
+=head1 COPYRIGHT
+
+Copyright (c) 1995 Graham Barr. All rights reserved. This program is free
+software; you can redistribute it and/or modify it under the same terms
+as Perl itself.
+
+=cut
+
+use strict;
+use vars qw($VERSION @ISA);
+require Exporter;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/);
+
+@ISA = qw(Exporter); # This is only so we can do version checking
+
+sub new
+{
+ my $self = shift;
+ my $type = ref($self) || $self;
+
+ my $vec = bless [''], $type;
+
+ $vec->add(@_)
+ if @_;
+
+ $vec;
+}
+
+sub add
+{
+ my $vec = shift;
+ my $f;
+
+ foreach $f (@_)
+ {
+ my $fn = $f =~ /^\d+$/ ? $f : fileno($f);
+ next
+ unless defined $fn;
+ vec($vec->[0],$fn++,1) = 1;
+ $vec->[$fn] = $f;
+ }
+}
+
+sub remove
+{
+ my $vec = shift;
+ my $f;
+
+ foreach $f (@_)
+ {
+ my $fn = $f =~ /^\d+$/ ? $f : fileno($f);
+ next
+ unless defined $fn;
+ vec($vec->[0],$fn++,1) = 0;
+ $vec->[$fn] = undef;
+ }
+}
+
+sub can_read
+{
+ my $vec = shift;
+ my $timeout = shift;
+
+ my $r = $vec->[0];
+
+ select($r,undef,undef,$timeout) > 0
+ ? _handles($vec, $r)
+ : ();
+}
+
+sub can_write
+{
+ my $vec = shift;
+ my $timeout = shift;
+
+ my $w = $vec->[0];
+
+ select(undef,$w,undef,$timeout) > 0
+ ? _handles($vec, $w)
+ : ();
+}
+
+sub has_error
+{
+ my $vec = shift;
+ my $timeout = shift;
+
+ my $e = $vec->[0];
+
+ select(undef,undef,$e,$timeout) > 0
+ ? _handles($vec, $e)
+ : ();
+}
+
+sub _max
+{
+ my($a,$b,$c) = @_;
+ $a > $b
+ ? $a > $c
+ ? $a
+ : $c
+ : $b > $c
+ ? $b
+ : $c;
+}
+
+sub select
+{
+ shift
+ if defined $_[0] && !ref($_[0]);
+
+ my($r,$w,$e,$t) = @_;
+ my @result = ();
+
+ my $rb = defined $r ? $r->[0] : undef;
+ my $wb = defined $w ? $e->[0] : undef;
+ my $eb = defined $e ? $w->[0] : undef;
+
+ if(select($rb,$wb,$eb,$t) > 0)
+ {
+ my @r = ();
+ my @w = ();
+ my @e = ();
+ my $i = _max(defined $r ? scalar(@$r) : 0,
+ defined $w ? scalar(@$w) : 0,
+ defined $e ? scalar(@$e) : 0);
+
+ for( ; $i > 0 ; $i--)
+ {
+ my $j = $i - 1;
+ push(@r, $r->[$i])
+ if defined $r->[$i] && vec($rb, $j, 1);
+ push(@w, $w->[$i])
+ if defined $w->[$i] && vec($wb, $j, 1);
+ push(@e, $e->[$i])
+ if defined $e->[$i] && vec($eb, $j, 1);
+ }
+
+ @result = (\@r, \@w, \@e);
+ }
+ @result;
+}
+
+sub _handles
+{
+ my $vec = shift;
+ my $bits = shift;
+ my @h = ();
+ my $i;
+
+ for($i = scalar(@$vec) - 1 ; $i > 0 ; $i--)
+ {
+ next unless defined $vec->[$i];
+ push(@h, $vec->[$i])
+ if vec($bits,$i - 1,1);
+ }
+
+ @h;
+}
+
+1;