Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

322 lines
7.7 KiB

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