home *** CD-ROM | disk | FTP | other *** search
/ Australian Personal Computer 2004 July / APC0407D2.iso / workshop / apache / files / ActivePerl-5.8.3.809-MSWin32-x86.msi / _6fba5a7eaee3d884f29e6905b578f457 < prev    next >
Encoding:
Text File  |  2004-02-02  |  5.9 KB  |  252 lines

  1. package File::CounterFile;
  2.  
  3. # $Id: CounterFile.pm,v 0.18 2003/10/06 12:47:40 gisle Exp $
  4.  
  5. require 5.004;
  6.  
  7. use strict;
  8.  
  9. use Carp   qw(croak);
  10. use Symbol qw(gensym);
  11. use Fcntl qw(O_RDWR O_CREAT);
  12.  
  13. use vars qw($VERSION $MAGIC $DEFAULT_INITIAL $DEFAULT_DIR);
  14.  
  15. sub Version { $VERSION; }
  16. $VERSION = "1.01";
  17.  
  18. $MAGIC = "#COUNTER-1.0\n";             # first line in counter files
  19. $DEFAULT_INITIAL = 0;                  # default initial counter value
  20.  
  21.  # default location for counter files
  22. $DEFAULT_DIR = $ENV{TMPDIR} || "/usr/tmp";
  23.  
  24. # Experimental overloading.
  25. use overload ('++'     => \&inc,
  26.           '--'     => \&dec,
  27.           '""'     => \&value,
  28.           fallback => 1,
  29.              );
  30.  
  31.  
  32. sub new
  33. {
  34.     my($class, $file, $initial) = @_;
  35.     croak("No file specified\n") unless defined $file;
  36.  
  37.     $file = "$DEFAULT_DIR/$file" unless $file =~ /^[\.\/]/;
  38.     $initial = $DEFAULT_INITIAL unless defined $initial;
  39.  
  40.     my $value;
  41.     local($/, $\) = ("\n", undef);
  42.     local *F;
  43.     sysopen(F, $file, O_RDWR|O_CREAT) or croak("Can't open $file: $!");
  44.     flock(F, 2) or croak("Can't flock: $!");
  45.     my $first_line = <F>;
  46.     if (defined $first_line) {
  47.     croak "Bad counter magic '$first_line' in $file" unless $first_line eq $MAGIC;
  48.     $value = <F>;
  49.     chomp($value);
  50.     }
  51.     else {
  52.     seek(F, 0, 0);
  53.     print F $MAGIC;
  54.     print F "$initial\n";
  55.     $value = $initial;
  56.     }
  57.     close(F) || croak("Can't close $file: $!");
  58.  
  59.     bless { file    => $file,  # the filename for the counter
  60.        'value'  => $value, # the current value
  61.         updated => 0,      # flag indicating if value has changed
  62.         # handle => XXX,   # file handle symbol. Only present when locked
  63.       };
  64. }
  65.  
  66.  
  67. sub locked
  68. {
  69.     exists shift->{handle};
  70. }
  71.  
  72.  
  73. sub lock
  74. {
  75.     my($self) = @_;
  76.     $self->unlock if $self->locked;
  77.  
  78.     my $fh = gensym();
  79.     my $file = $self->{file};
  80.  
  81.     open($fh, "+<$file") or croak "Can't open $file: $!";
  82.     flock($fh, 2) or croak "Can't flock: $!";  # 2 = exlusive lock
  83.  
  84.     local($/) = "\n";
  85.     my $magic = <$fh>;
  86.     if ($magic ne $MAGIC) {
  87.     $self->unlock;
  88.     croak("Bad counter magic '$magic' in $file");
  89.     }
  90.     chomp($self->{'value'} = <$fh>);
  91.  
  92.     $self->{handle}  = $fh;
  93.     $self->{updated} = 0;
  94.     $self;
  95. }
  96.  
  97.  
  98. sub unlock
  99. {
  100.     my($self) = @_;
  101.     return unless $self->locked;
  102.  
  103.     my $fh = $self->{handle};
  104.  
  105.     if ($self->{updated}) {
  106.     # write back new value
  107.     local($\) = undef;
  108.     seek($fh, 0, 0) or croak "Can't seek to beginning: $!";
  109.     print $fh $MAGIC;
  110.     print $fh "$self->{'value'}\n";
  111.     }
  112.  
  113.     close($fh) or warn "Can't close: $!";
  114.     delete $self->{handle};
  115.     $self;
  116. }
  117.  
  118.  
  119. sub inc
  120. {
  121.     my($self) = @_;
  122.  
  123.     if ($self->locked) {
  124.     $self->{'value'}++;
  125.     $self->{updated} = 1;
  126.     } else {
  127.     $self->lock;
  128.     $self->{'value'}++;
  129.     $self->{updated} = 1;
  130.     $self->unlock;
  131.     }
  132.     $self->{'value'}; # return value
  133. }
  134.  
  135.  
  136. sub dec
  137. {
  138.     my($self) = @_;
  139.  
  140.     if ($self->locked) {
  141.     unless ($self->{'value'} =~ /^\d+$/) {
  142.         $self->unlock;
  143.         croak "Autodecrement is not magical in perl";
  144.     }
  145.     $self->{'value'}--;
  146.     $self->{updated} = 1;
  147.     }
  148.     else {
  149.     $self->lock;
  150.     unless ($self->{'value'} =~ /^\d+$/) {
  151.         $self->unlock;
  152.         croak "Autodecrement is not magical in perl";
  153.     }
  154.     $self->{'value'}--;
  155.     $self->{updated} = 1;
  156.     $self->unlock;
  157.     }
  158.     $self->{'value'}; # return value
  159. }
  160.  
  161.  
  162. sub value
  163. {
  164.     my($self) = @_;
  165.     my $value;
  166.     if ($self->locked) {
  167.     $value = $self->{'value'};
  168.     }
  169.     else {
  170.     $self->lock;
  171.     $value = $self->{'value'};
  172.     $self->unlock;
  173.     }
  174.     $value;
  175. }
  176.  
  177.  
  178. sub DESTROY
  179. {
  180.     my $self = shift;
  181.     $self->unlock;
  182. }
  183.  
  184. 1;
  185.  
  186. __END__
  187.  
  188. =head1 NAME
  189.  
  190. File::CounterFile - Persistent counter class
  191.  
  192. =head1 SYNOPSIS
  193.  
  194.  use File::CounterFile;
  195.  $c = File::CounterFile->new("COUNTER", "aa00");
  196.  
  197.  $id = $c->inc;
  198.  open(F, ">F$id");
  199.  
  200. =head1 DESCRIPTION
  201.  
  202. This module implements a persistent counter class.  Each counter is
  203. represented by a separate file in the file system.  File locking is
  204. applied, so multiple processes might try to access the same counters
  205. at the same time without risk of counter destruction.
  206.  
  207. You give the file name as the first parameter to the object
  208. constructor (C<new>).  The file is created if it does not exist.
  209.  
  210. If the file name does not start with "/" or ".", then it is
  211. interpreted as a file relative to C<$File::CounterFile::DEFAULT_DIR>.
  212. The default value for this variable is initialized from the
  213. environment variable C<TMPDIR>, or F</usr/tmp> is no environment
  214. variable is defined.  You may want to assign a different value to this
  215. variable before creating counters.
  216.  
  217. If you pass a second parameter to the constructor, that sets the
  218. initial value for a new counter.  This parameter only takes effect
  219. when the file is created (i.e. it does not exist before the call).
  220.  
  221. When you call the C<inc()> method, you increment the counter value by
  222. one. When you call C<dec()> the counter value is decrementd.  In both
  223. cases the new value is returned.  The C<dec()> method only works for
  224. numerical counters (digits only).
  225.  
  226. You can peek at the value of the counter (without incrementing it) by
  227. using the C<value()> method.
  228.  
  229. The counter can be locked and unlocked with the C<lock()> and
  230. C<unlock()> methods.  Incrementing and value retrieval is faster when
  231. the counter is locked, because we do not have to update the counter
  232. file all the time.  You can query whether the counter is locked with
  233. the C<locked()> method.
  234.  
  235. There is also an operator overloading interface to the
  236. File::CounterFile object.  This means that you might use the C<++>
  237. operator for incrementing the counter, C<--> operator for decrementing
  238. and you can interpolate counters diretly into strings.
  239.  
  240. =head1 COPYRIGHT
  241.  
  242. Copyright (c) 1995-1998,2002,2003 Gisle Aas. All rights reserved.
  243.  
  244. This library is free software; you can redistribute it and/or
  245. modify it under the same terms as Perl itself.
  246.  
  247. =head1 AUTHOR
  248.  
  249. Gisle Aas <gisle@aas.no>
  250.  
  251. =cut
  252.