From 3f3c4cc8c217530e07ad722133766f63794b44de Mon Sep 17 00:00:00 2001 From: Mike Taylor Date: Tue, 9 Dec 2014 12:47:44 +0000 Subject: [PATCH 1/1] Import original source of IndexData-Utils 0.01 --- .gitignore | 5 ++ Changes | 6 ++ MANIFEST | 8 ++ Makefile.PL | 12 +++ README | 26 ++++++ lib/IndexData/Utils.pm | 47 ++++++++++ lib/IndexData/Utils/PersistentCounter.pm | 137 ++++++++++++++++++++++++++++++ t/01-IndexData-Utils.t | 8 ++ t/02-IndexData-Utils-PersistentCounter.t | 49 +++++++++++ 9 files changed, 298 insertions(+) create mode 100644 .gitignore create mode 100644 Changes create mode 100644 MANIFEST create mode 100644 Makefile.PL create mode 100644 README create mode 100644 lib/IndexData/Utils.pm create mode 100644 lib/IndexData/Utils/PersistentCounter.pm create mode 100644 t/01-IndexData-Utils.t create mode 100644 t/02-IndexData-Utils-PersistentCounter.t diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ed8c12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +MYMETA.json +MYMETA.yml +Makefile +blib +pm_to_blib diff --git a/Changes b/Changes new file mode 100644 index 0000000..933ee96 --- /dev/null +++ b/Changes @@ -0,0 +1,6 @@ +Revision history for Perl extension IndexData::Utils. + +0.01 Mon Dec 8 12:18:26 2014 + - original version; created by h2xs 1.23 with options + -X IndexData::Utils + diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..3ae38c6 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,8 @@ +Changes +MANIFEST +Makefile.PL +README +lib/IndexData/Utils.pm +lib/IndexData/Utils/PersistentCounter.pm +t/01-IndexData-Utils.t +t/02-IndexData-Utils-PersistentCounter.t diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..5a1db51 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,12 @@ +use 5.018002; +use ExtUtils::MakeMaker; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. +WriteMakefile( + NAME => 'IndexData::Utils', + VERSION_FROM => 'lib/IndexData/Utils.pm', # finds $VERSION + PREREQ_PM => {}, # e.g., Module::Name => 1.1 + ($] >= 5.005 ? ## Add these new keywords supported since 5.005 + (ABSTRACT_FROM => 'lib/IndexData/Utils.pm', # retrieve abstract from module + AUTHOR => 'Mike Taylor ') : ()), +); diff --git a/README b/README new file mode 100644 index 0000000..f449964 --- /dev/null +++ b/README @@ -0,0 +1,26 @@ +IndexData-Utils +=============== + +This library contains utility functions that we at Index Data want to +share between multiple applications -- for example, the persistent +counter used by both IRSpy (see issue IR-350) and MKHome (IR-351). + + +INSTALLATION + +To install this module type the following: + + perl Makefile.PL + make + make test + make install + +COPYRIGHT AND LICENCE + +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. + + diff --git a/lib/IndexData/Utils.pm b/lib/IndexData/Utils.pm new file mode 100644 index 0000000..4e61396 --- /dev/null +++ b/lib/IndexData/Utils.pm @@ -0,0 +1,47 @@ +package IndexData::Utils; + +use 5.018002; +use strict; +use warnings; + +our $VERSION = '0.01'; + +use IndexData::Utils::PersistentCounter; + +1; +__END__ + + +=head1 NAME + +IndexData::Utils - Utility Perl extension for Index Data applications + +=head1 SYNOPSIS + + use IndexData::Utils; + # Use functions from the various submodules. + +=head1 DESCRIPTION + +This library contains utility functions that we at Index Data want to +share between multiple applications -- for example, the persistent +counter used by both IRSpy (see issue IR-350) and MKHome (IR-351). + +=head1 SEE ALSO + +IndexData::Utils::PersistentCounter + +=head1 AUTHOR + +Mike Taylor, Emike@indexdata.comE + +=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 diff --git a/lib/IndexData/Utils/PersistentCounter.pm b/lib/IndexData/Utils/PersistentCounter.pm new file mode 100644 index 0000000..e8223f9 --- /dev/null +++ b/lib/IndexData/Utils/PersistentCounter.pm @@ -0,0 +1,137 @@ +package IndexData::Utils::PersistentCounter; + +use 5.018002; +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, 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 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, Emike@indexdata.comE + +=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; diff --git a/t/01-IndexData-Utils.t b/t/01-IndexData-Utils.t new file mode 100644 index 0000000..a3496a9 --- /dev/null +++ b/t/01-IndexData-Utils.t @@ -0,0 +1,8 @@ +use strict; +use warnings; + +use Test::More tests => 1; +BEGIN { use_ok('IndexData::Utils') }; + +# Nothing more to test for this, unless perhaps that the submodules +# have been loaded. diff --git a/t/02-IndexData-Utils-PersistentCounter.t b/t/02-IndexData-Utils-PersistentCounter.t new file mode 100644 index 0000000..8b57c30 --- /dev/null +++ b/t/02-IndexData-Utils-PersistentCounter.t @@ -0,0 +1,49 @@ +use strict; +use warnings; + +use Test::More tests => 17; +BEGIN { use_ok('IndexData::Utils::PersistentCounter') }; + +my $file = "/tmp/id-u-pc-$$"; +my $counter = new IndexData::Utils::PersistentCounter($file); +ok(!defined $counter, "can't open non-existent counter"); + +$counter = new IndexData::Utils::PersistentCounter("/x/$file", 1); +ok(!defined $counter, "can't create counter in silly place"); + +$counter = new IndexData::Utils::PersistentCounter($file, 1); +my $detail = defined $counter ? '' : ": $!@"; +ok(defined $counter, "created new counter$detail"); + +foreach my $i (1..5) { + my $n = $counter->next(); + ok(defined $n, "n is defined"); + ok($n == $i, "n has correct value $i"); +} + +# Three processes making five accesses each +for (my $i = 0; $i < 3; $i ++) { + my $pid = fork(); + if ($pid == 0) { + # child + foreach my $j (1..5) { + my $n = $counter->next(); + print "# child ", $i+1, ", access ", $j+1, ": value is $n\n"; + } + exit 0; + } else { + print "# process $pid started\n"; + } +} + +while ((my $pid = wait()) > 0) { + print "# process $pid completed\n"; +} + +my $n = $counter->next(); +ok($n == 21, "n == 21 on 21 total access (n=$n)"); + +my $ok = $counter->delete(); +ok($ok, "deleted counter file"); +$counter = new IndexData::Utils::PersistentCounter($file); +ok(!defined $counter, "can't open deleted counter"); -- 1.7.10.4