# -*- cperl -*-
# Copyright (C) 2006 MySQL AB
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

package mtr_unique;

use strict;
use Fcntl ':flock';

use base qw(Exporter);
our @EXPORT= qw(mtr_get_unique_id mtr_release_unique_id);

use My::Platform;

sub msg {
 # print "### unique($$) - ", join(" ", @_), "\n";
}

my $file= "/tmp/mysql-test-ports";

my %mtr_unique_ids;

END {
  my $allocated_id= $mtr_unique_ids{$$};
  if (defined $allocated_id)
  {
    mtr_release_unique_id($allocated_id);
  }
  delete $mtr_unique_ids{$$};
}

#
# Get a unique, numerical ID, given a file name (where all
# requested IDs are stored), a minimum and a maximum value.
#
# If no unique ID within the specified parameters can be
# obtained, return undef.
#
sub mtr_get_unique_id($$) {
  my ($min, $max)= @_;;

  msg("get, '$file', $min-$max");

  die "Can only get one unique id per process!" if $mtr_unique_ids{$$};

  my $ret = undef;
  my $changed = 0;

  if(eval("readlink '$file'") || eval("readlink '$file.sem'")) {
    die 'lock file is a symbolic link';
  }

  chmod 0777, "$file.sem";
  open SEM, ">", "$file.sem" or die "can't write to $file.sem";
  flock SEM, LOCK_EX or die "can't lock $file.sem";
  if(! -e $file) {
    open FILE, ">", $file or die "can't create $file";
    close FILE;
  }

  msg("HAVE THE LOCK");

  if(eval("readlink '$file'") || eval("readlink '$file.sem'")) {
    die 'lock file is a symbolic link';
  }

  chmod 0777, $file;
  open FILE, "+<", $file or die "can't open $file";
  #select undef,undef,undef,0.2;
  seek FILE, 0, 0;
  my %taken = ();
  while(<FILE>) {
    chomp;
    my ($id, $pid) = split / /;
    $taken{$id} = $pid;
    msg("taken: $id, $pid");
    # Check if process with given pid is alive
    if(!process_alive($pid)) {
      print "Removing slot $id used by missing process $pid\n";
      msg("Removing slot $id used by missing process $pid");
      delete $taken{$id};
      $changed++;
    }
  }
  for(my $i=$min; $i<=$max; ++$i) {
    if(! exists $taken{$i}) {
      $ret = $i;
      $taken{$i} = $$;
      $changed++;
      # Remember the id this process got
      $mtr_unique_ids{$$}= $i;
      msg(" got $i"); 
      last;
    }
  }
  if($changed) {
    seek FILE, 0, 0;
    truncate FILE, 0 or die "can't truncate $file";
    for my $k (keys %taken) {
      print FILE $k . ' ' . $taken{$k} . "\n";
    }
  }
  close FILE;

  msg("RELEASING THE LOCK");
  flock SEM, LOCK_UN or warn "can't unlock $file.sem";
  close SEM;

  return $ret;
}


#
# Release a unique ID.
#
sub mtr_release_unique_id($) {
  my ($myid)= @_;

  msg("release, $myid");


  if(eval("readlink '$file'") || eval("readlink '$file.sem'")) {
    die 'lock file is a symbolic link';
  }

  open SEM, ">", "$file.sem" or die "can't write to $file.sem";
  flock SEM, LOCK_EX or die "can't lock $file.sem";

  msg("HAVE THE LOCK");

  if(eval("readlink '$file'") || eval("readlink '$file.sem'")) {
    die 'lock file is a symbolic link';
  }

  if(! -e $file) {
    open FILE, ">", $file or die "can't create $file";
    close FILE;
  }
  open FILE, "+<", $file or die "can't open $file";
  #select undef,undef,undef,0.2;
  seek FILE, 0, 0;
  my %taken = ();
  while(<FILE>) {
    chomp;
    my ($id, $pid) = split / /;
    msg(" taken, $id $pid");
    $taken{$id} = $pid;
  }

  if ($taken{$myid} != $$)
  {
    msg(" The unique id for this process does not match pid");
  }


  msg(" removing $myid");
  delete $taken{$myid};
  seek FILE, 0, 0;
  truncate FILE, 0 or die "can't truncate $file";
  for my $k (keys %taken) {
    print FILE $k . ' ' . $taken{$k} . "\n";
  }
  close FILE;

  msg("RELEASE THE LOCK");

  flock SEM, LOCK_UN or warn "can't unlock $file.sem";
  close SEM;
}


1;

