package PhotoPub::ParallelLimit;
require Exporter;
@ISA = qw(Exporter);
@EXPORT_OK = qw(&enter &leave &clearlocks LIM_MOGRIFY LIM_CGI);

use strict;

use PhotoPub::Global qw(&register_init $error);

use IPC::Semaphore;
use IPC::SysV qw(&ftok SEM_UNDO IPC_CREAT S_IRWXU);

use constant LIM_MOGRIFY => 0;
use constant LIM_CGI => 1;

# a 'unique' ID chosen for Photopub
my $appid=0x4d9;

my @sems=();
my @undo=();

sub getsem {
  my ($lockid,$limit)=@_;

  my $sem=$sems[$lockid];
  if(!defined $sem) {
    # $> is our EUID
    my $key=$appid<<20 | $> << 4 | $lockid;

    $sem=new IPC::Semaphore($key,3,S_IRWXU);

    if(!defined $sem) {
      $sem=new IPC::Semaphore($key,3,S_IRWXU | IPC_CREAT);
      &$error(type=>'config',
	      message=>"Couldn't create semaphore") unless defined $sem;
      $sem->setall($limit,$limit,1);
    }
    $sems[$lockid]=$sem;
    $undo[$lockid]=0;
  } else {
    my $oldlimit=$sem->getval(1);
    if($limit!=$oldlimit) {
      # limit got changed in config; grab lock and check again
      $sem->op(2,-1,SEM_UNDO);
      $oldlimit=$sem->getval(1);
      if($limit!=$oldlimit) {
	$sem->op(0,$limit-$oldlimit,0,
		 1,$limit-$oldlimit,0);
      }
      $sem->op(2,1,SEM_UNDO);
    }
  }
}

sub enter {
  my ($lockid,$limit)=@_;
  return unless defined $limit;
  &getsem($lockid,$limit);
  my $sem=$sems[$lockid];
  return unless defined $sem;
  $sem->op(0,-1,SEM_UNDO);
  $undo[$lockid]++;
}

sub leave {
  my ($lockid)=@_;
  my $sem=$sems[$lockid];
  return unless defined $sem;
  return unless $undo[$lockid]>0;
  $sem->op(0,1,SEM_UNDO);
  $undo[$lockid]--;
}

sub clearlocks {
  for my $i (0..$#sems) {
    $sems[$i]->op(0,$undo[$i],SEM_UNDO) if defined $undo[$i] && $undo[$i]>0;
  }
}

&register_init('cleanup',\&clearlocks);
