*sigh* downgrade required Perl version yet again, in all three places, to the v5...
[perl-indexdata-utils.git] / lib / IndexData / Utils / PersistentCounter.pm
index 5779261..2eaac65 100644 (file)
@@ -1,7 +1,137 @@
 package IndexData::Utils::PersistentCounter;
 
-use 5.018002;
+use 5.008008;
 use strict;
 use warnings;
 
+use IO::File;
+
+
+=head1 NAME
+
+IndexData::Utils::PersistentCounter - Perl extension imnlementing persistent counters
+
+=head1 SYNOPSIS
+
+  use IndexData::Utils::PersistentCounter;
+  $counter = new IndexData::Utils::PersistentCounter($file, 1);
+  $n = $counter->next();
+  $n = $counter->next();
+  # ...
+  $n = $counter->delete();
+
+=head1 DESCRIPTION
+
+This library provides a simple persistent counter class for
+maintaining a counter on disk across multiple runs of a program. It is
+safe against multiple concurrent accesses (i.e. will not issue the
+same value twice to different processes). It can be used for
+applications such as generating unique record IDs.
+
+=head1 METHODS
+
+=head2 new()
+
+  $old = new IndexData::Utils::PersistentCounter($file1);
+  $new = new IndexData::Utils::PersistentCounter($file2, 1);
+
+Creates a new counter object associated with a file which contains the
+persistent state of the counter. The purpose of the counter is to
+return consecutive integers on consecutive calls, even if those calls
+are made from multiple concurrent processes. The file stores the state
+across invocations.
+
+In the usual case (no second argument), the file must already exist;
+if it does not, it is not created, but an undefined value is returned.
+
+If a second argument is provided and its value is true, then a new
+counter file is created with initial value 1. Note that B<this will
+overwrite any existing file>, so use with caution.
+
+=cut
+
+sub new {
+    my $class = shift();
+    my($file, $create) = @_;
+
+    if (! -f $file) {
+       return undef if !$create;
+       #   ### There is a bit of a race condition here, but it's not
+       #       something that's going to crop up in real life.
+       my $fh = new IO::File(">$file") || return undef;
+       $fh->print("1\n");
+       $fh->close() or return undef;
+    }
+
+    my $this = bless {
+       file => $file,
+    }, $class;
+
+    return $this;
+}
+
+
+=head2 next()
+
+  $n = $counter->next();
+
+Returns the next available integer from the specified counter, and
+increments the counter ready for the next invocation (whether that
+invocation is in this process or a different one).
+
+The first call of C<next()> on a newly created counter returns 1, not
+0. Each subsequent call returns a value one higher than the previous
+call.
+
+=cut
+
+sub next {
+    my $this = shift();
+
+    my $fh = new IO::File('+<' . $this->{file}) || return undef;
+    flock($fh, 2) || die "can't lock file";
+    my $n = <$fh>;
+    $fh->seek(0, 0);
+    $fh->print($n+1, "\n");
+    $fh->close() or return undef;
+    return $n+0;
+}
+
+
+=head2 delete()
+
+  $ok = $counter->delete();
+
+Permanently deletes a counter file. Returns true if the deletion was
+successful, false otherwise.
+
+=cut
+
+sub delete {
+    my $this = shift();
+
+    unlink $this->{file} or return 0;
+    return 1;
+}
+
+
+=head1 SEE ALSO
+
+IndexData::Utils
+
+=head1 AUTHOR
+
+Mike Taylor, E<lt>mike@indexdata.comE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2014 by Index Data.
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.8.4 or,
+at your option, any later version of Perl 5 you may have available.
+
+
+=cut
+
 1;