home *** CD-ROM | disk | FTP | other *** search
- # ======================================================================
- #
- # Copyright (C) 2000 Lincoln D. Stein
- #
- # ======================================================================
-
- package IO::SessionSet;
-
- use strict;
- use Carp;
- use IO::Select;
- use IO::Handle;
- use IO::SessionData;
-
- use vars '$DEBUG';
- $DEBUG = 0;
-
- # Class method new()
- # Create a new Session set.
- # If passed a listening socket, use that to
- # accept new IO::SessionData objects automatically.
- sub new {
- my $pack = shift;
- my $listen = shift;
- my $self = bless {
- sessions => {},
- readers => IO::Select->new(),
- writers => IO::Select->new(),
- },$pack;
- # if initialized with an IO::Handle object (or subclass)
- # then we treat it as a listening socket.
- if ( defined($listen) and $listen->can('accept') ) {
- $self->{listen_socket} = $listen;
- $self->{readers}->add($listen);
- }
- return $self;
- }
-
- # Object method: sessions()
- # Return list of all the sessions currently in the set.
- sub sessions { return values %{shift->{sessions}} };
-
- # Object method: add()
- # Add a handle to the session set. Will automatically
- # create a IO::SessionData wrapper around the handle.
- sub add {
- my $self = shift;
- my ($handle,$writeonly) = @_;
- warn "Adding a new session for $handle.\n" if $DEBUG;
- return $self->{sessions}{$handle} = $self->SessionDataClass->new($self,$handle,$writeonly);
- }
-
- # Object method: delete()
- # Remove a session from the session set. May pass either a handle or
- # a corresponding IO::SessionData wrapper.
- sub delete {
- my $self = shift;
- my $thing = shift;
- my $handle = $self->to_handle($thing);
- my $sess = $self->to_session($thing);
- warn "Deleting session $sess handle $handle.\n" if $DEBUG;
- delete $self->{sessions}{$handle};
- $self->{readers}->remove($handle);
- $self->{writers}->remove($handle);
- }
-
- # Object method: to_handle()
- # Return a handle, given either a handle or a IO::SessionData object.
- sub to_handle {
- my $self = shift;
- my $thing = shift;
- return $thing->handle if $thing->isa('IO::SessionData');
- return $thing if defined (fileno $thing);
- return; # undefined value
- }
-
- # Object method: to_session
- # Return a IO::SessionData object, given either a handle or the object itself.
- sub to_session {
- my $self = shift;
- my $thing = shift;
- return $thing if $thing->isa('IO::SessionData');
- return $self->{sessions}{$thing} if defined (fileno $thing);
- return; # undefined value
- }
-
- # Object method: activate()
- # Called with parameters ($session,'read'|'write' [,$activate])
- # If called without the $activate argument, will return true
- # if the indicated handle is on the read or write IO::Select set.
- # May use either a session object or a handle as first argument.
- sub activate {
- my $self = shift;
- my ($thing,$rw,$act) = @_;
- croak 'Usage $obj->activate($session,"read"|"write" [,$activate])'
- unless @_ >= 2;
- my $handle = $self->to_handle($thing);
- my $select = lc($rw) eq 'read' ? 'readers' : 'writers';
- my $prior = defined $self->{$select}->exists($handle);
- if (defined $act && $act != $prior) {
- $self->{$select}->add($handle) if $act;
- $self->{$select}->remove($handle) unless $act;
- warn $act ? 'Activating' : 'Inactivating',
- " handle $handle for ",
- $rw eq 'read' ? 'reading':'writing',".\n" if $DEBUG;
- }
- return $prior;
- }
-
- # Object method: wait()
- # Wait for I/O. Handles writes automatically. Returns a list of IO::SessionData
- # objects ready for reading.
- # If there is a listen socket, then will automatically do an accept() and return
- # a new IO::SessionData object for that.
- sub wait {
- my $self = shift;
- my $timeout = shift;
-
- # Call select() to get the list of sessions that are ready for reading/writing.
- croak "IO::Select->select() returned error: $!"
- unless my ($read,$write) =
- IO::Select->select($self->{readers},$self->{writers},undef,$timeout);
-
- # handle queued writes automatically
- foreach (@$write) {
- my $session = $self->to_session($_);
- warn "Writing pending data (",$session->pending+0," bytes) for $_.\n" if $DEBUG;
- my $rc = $session->write;
- }
-
- # Return list of sessions that are ready for reading.
- # If one of the ready handles is the listen socket, then
- # create a new session.
- # Otherwise return the ready handles as a list of IO::SessionData objects.
- my @sessions;
- foreach (@$read) {
- if ($_ eq $self->{listen_socket}) {
- my $newhandle = $_->accept;
- warn "Accepting a new handle $newhandle.\n" if $DEBUG;
- my $newsess = $self->add($newhandle) if $newhandle;
- push @sessions,$newsess;
- } else {
- push @sessions,$self->to_session($_);
- }
- }
- return @sessions;
- }
-
- # Class method: SessionDataClass
- # Return the string containing the name of the session data
- # wrapper class. Subclass and override to use a different
- # session data class.
- sub SessionDataClass { return 'IO::SessionData'; }
-
- 1;
-