
use IO::Socket;  # note:  not enough to have "use Socket"

package DSMClnt;

# class variables
$NumNodes;  # number of application nodes
$SvrSkt;  # socket from this application node to the server

# subroutine to set up socket to server 
sub DSMCheckIn {
   # turn buffering off
   $| = 1;
   my $SrvrIP  = $ARGV[0];  # name of the server
   my $Port = $ARGV[1];
   # note that $SvrSkt will contain the socket ID for the application
   # node which is currently executing this code, i.e. it will be
   # different for each node
   $SvrSkt = new IO::Socket::INET (PeerAddr=>$SrvrIP,
                                PeerPort=>$Port,
                                Proto=>'tcp');
   print $SvrSkt "checkin 0 0\n";
   my $Response = <$SvrSkt>;
   ($NumNodes,$MyNode) = split(" ",$Response);
   return ($SvrSkt,$NumNodes,$MyNode);
}

sub DSMExit {
   print $SvrSkt "close 0 0\n";
   close $SvrSkt;
   exit;
}

# each shared variable in a PerlDSM application program is represented
# internally as a set of object of this class (note that there will be a
# different object at each node, since the application program is
# running at each node), and tied to an ordinary Perl variable used by
# the application program; arguments to "tie" are (the tied variable is
# listed first but is not considered an argument):
#   the class name (in single quotes), 'DSMClnt' 
#   variable type -- 0 for scalar, 1 for array, 2 for hash
#   the name of the tied/shared variable in single quotes; if array or
#      hash element, give only the base, e.g. '$x' instead of $x[i]'
#   subscript, if array or hash element 
sub TIESCALAR {

   # get arguments
   my $Class = shift;
   my $VarType = shift;
   my $VarName = shift;
   my $Subscript = shift;

   # the instance variables for this class will be:
   #    variable type (0, 1, 2)
   #    variable name/name base
   #    subscript (-1 for scalar)
   my $R = {VrTyp=>$VarType, VrNm=>$VarName, SbScrpt=>$Subscript};

   # notify server about this variable, including subscript if any
   my $FullName = GetFullName($VarType,$VarName,$Subscript);
   print $SvrSkt "create ", $FullName, " 0\n";
   my $Response = <$SvrSkt>;  # ACK
   # bless the reference to type, name and subscript with the package name
   bless $R, $Class;
   return $R;
}

sub FETCH {
   # get reference to this object from the argument
   my $R = shift;
   my $VarType = $R->{VrTyp}; 
   my $VarName = $R->{VrNm}; 
   my $Subscript = $R->{SbScrpt}; 
   my $FullName = GetFullName($VarType,$VarName,$Subscript);
   print $SvrSkt "read ", $FullName, " 0\n";
   my $Response = <$SvrSkt>;
   return $Response;
}

sub STORE {
   # get reference to this object from the argument
   my $R = shift;
   my $VarType = $R->{VrTyp}; 
   my $VarName = $R->{VrNm}; 
   my $Subscript = $R->{SbScrpt}; 
   # set the data to the second argument, dereferencing the pointer
   my $D = shift;
   my $FullName = GetFullName($VarType,$VarName,$Subscript);
   print $SvrSkt "write ", $FullName, " ", $D, "\n";
   my $Response = <$SvrSkt>;
}

sub GetFullName  {
   my $Type = shift;
   my $Name = shift;
   my $Sb = shift;
   if ($Type == 0)  {
      return $Name;
   }
   if ($Type == 1)  {
      $Name .= "[";
      $Name .= $Sb;
      $Name .= "]";
      return $Name;
   }
   $Name .= "{";
   $Name .= $Sb;
   $Name .= "}";
   return $Name;
}

1;  # needed due to quirk in Perl

