#$Id: Scoreboard.pm 217 2007-06-30 06:46:33Z fil $
########################################################
package POE::Component::Daemon::Scoreboard;

use 5.00405;
use strict;

use vars qw($VERSION $UNIQUE);

use IPC::SysV qw(IPC_PRIVATE S_IRWXU IPC_CREAT SEM_UNDO);
use Carp;

$VERSION = '0.01';

sub DEBUG () { 0 }

########################################################
sub new
{
    my($package, $N)=@_;
    if($UNIQUE) {
        warn "This should be only one $package.  Reusing previous one.";
        return $UNIQUE;
    }

    my $self=bless {N=>$N}, $package;

    # On linux, 2.6 kernels (at least), the first call after a reboot will
    # fail, second and subsequent will succeed.
    $self->{mem} = shmget(IPC_PRIVATE, $N, S_IRWXU) 
                        || 
                   shmget(IPC_PRIVATE, $N, S_IRWXU);
    die "$$: Unable to create shared memory: $!\n" unless $self->{mem};

    $self->{slots}=[reverse 0..($N-1)];

    my $blank=' ' x $N;
    shmwrite($self->{mem}, $blank, 0, $N);

    $UNIQUE=$self;

    return $self;
}

########################################################
sub read_all
{
    my($self)=@_;

    my $str=" " x $self->{N};
    shmread($self->{mem}, $str, 0, $self->{N})
        or die "Unable to read shared memory: $!\n";

    my $ret=[split //, $str];
    return $ret;
}

########################################################
sub add
{
    my($self, $value)=@_;
    return unless @{$self->{slots}};
    my $slot=pop @{$self->{slots}};
    DEBUG and warn "Adding slot $slot";
    $self->write($slot, $value);
    return $slot;
}

########################################################
sub drop
{
    my($self, $slot)=@_;
    if($slot >= $self->{N}) {
        carp "$slot isn't a known slot\n";
        return;
    }
    $self->write($slot, '.');
    DEBUG and warn "Dropped slot $slot";
    push @{$self->{slots}}, $slot;
    return;
}

########################################################
sub write
{
    my($self, $slot, $value)=@_;
    if($slot >= $self->{N}) {
        carp "$slot isn't a known slot\n";
        return;
    }

    $value=substr($value, 0, 1);
    DEBUG and warn "Setting slot $slot to $value";

    shmwrite($self->{mem}, $value, $slot, 1)
        or warn "Writing shared memory slot $slot: $!";

    return;
}

########################################################
sub read
{
    my($self, $slot)=@_;
    return unless defined $slot;

    if($slot >= $self->{N}) {
        carp "$slot isn't a known slot\n";
        return;
    }
    DEBUG and warn "Reading value $slot";

    my $value=" ";
    shmread($self->{mem}, $value, $slot, 1)
        or warn "Reading shared memory slot $slot: $!";
    return $value;
}

########################################################
sub status
{
    my($self)=@_;
    my @ret;

    my $n=$self->read_all();
    push @ret, ref($self);
    push @ret, "$self->{N} slots in the scoreboard";
    push @ret, join '', "Slots [", @$n, "]";
    push @ret, (0+@{$self->{slots}})." slots free";

    return join "\n    ", @ret;
}

1;

__DATA__

