File Coverage

lib/Tie/FileLRUCache.pm
Criterion Covered Total %
statement 278 377 73.7
branch 80 112 71.4
condition 20 33 60.6
subroutine 29 31 93.5
pod 9 9 100.0
total 416 562 74.0


line stmt bran cond sub pod time code
1             package Tie::FileLRUCache;
2              
3 1     1   77327 use strict;
  1         3  
  1         48  
4              
5 1     1   1606 use Class::ParmList qw (simple_parms parse_parms);
  1         3320  
  1         93  
6 1     1   3147 use Digest::SHA1 qw(sha1_hex);
  1         4433  
  1         76  
7 1     1   7 use Fcntl qw (:flock);
  1         2  
  1         145  
8 1     1   5 use File::Spec;
  1         2  
  1         21  
9 1     1   1532 use Storable qw (nstore nfreeze retrieve);
  1         4817  
  1         85  
10 1     1   9 use Symbol qw (gensym);
  1         3  
  1         54  
11              
12 1     1   6 use vars qw ($VERSION);
  1         2  
  1         47  
13              
14             BEGIN {
15 1     1   5291 $VERSION = "1.05";
16             }
17              
18             ###########################################################################
19              
20             =head1 NAME
21              
22             Tie::FileLRUCache - A lightweight but robust filesystem based persistent LRU cache
23              
24             =head1 CHANGES
25              
26             1.05 2005.09.14 - Changes to pod tests to make them more CPANTS friendly.
27             No functional changes.
28              
29             1.04 2005.09.13 - Removed use of 'warnings' to fix compatibility with Perl 5.005.
30             Fixed minor typographical errors in documentation.
31              
32             1.03 2005.09.10 - Changed build test to handle difference in treatment of hashes
33             in scalar contect between 5.6.x and 5.8.x versions of Perl that
34             caused a test failure under Perl 5.6.x.
35              
36             1.02 2005.09.08 - Added build tests. Major code cleanup. Improved platform portability.
37             Added and documented 'cache_dir', 'keep_last' and 'number_of_entries'
38             methods. Added Module::Build support.
39              
40             1.01 1999.12.09 - Added 'detainting' to cache management code.
41              
42             =head1 SYNOPSIS
43              
44             =head2 OBJECT INTERFACE
45              
46             use Tie::FileLRUCache;
47             my $cache = Tie::FileLRUCache->new({ -cache_dir => $directory, -keep_last => 100 });
48              
49             # Inserting value into LRU cache using '-key'
50             $cache->update({ -key => $key, -value => $value });
51              
52              
53             # Inserting value into LRU cache using '-cache_key'
54             my $cache_key = $cache->make_cache_key({ -key => $key });
55             $cache->update({ -cache_key => $cache_key, -value => $value });
56              
57              
58             # Checking LRU cache
59             my ($in_cache,$value) = $cache->check({ -key => $key });
60             if ($in_cache) {
61             return $value;
62             }
63             # Not in cache - do something else
64              
65              
66             # Checking LRU cache with speed up hack for objects, hashes, arrays etc used as keys
67             my $cache_key = $cache->make_cache_key({ -key => $something });
68             my ($in_cache,$value) = $cache->check({ -cache_key => $cache_key });
69             if ($in_cache) {
70             return $value;
71             }
72             # Not in cache - do something else
73              
74              
75             # Deleting a key and its value from the cache
76             $cache->delete({ -key => $key });
77              
78              
79             # Clearing LRU cache
80             $cache->clear;
81              
82             =head2 TIED INTERFACE
83              
84             use Tie::FileLRUCache;
85              
86             [$X =] tie %hash, 'Tie::FileLRUCache', $cache_dir, $keep_last_n;
87              
88             # Adding a key/value to the cache
89             $hash{$key} = $value;
90              
91             # Checking the cache
92             if (not exists $hash{$key}) {;
93             # No match
94             .
95             .
96             .
97              
98             } else {
99             my $value = $hash{$key};
100             .
101             .
102             .
103              
104             }
105              
106             # Removing a value from the cache;
107             delete $hash{$key};
108              
109             # Clearing the cache
110             %hash = ();
111              
112             Note: Iteration over the cache (each, keys, values) is _NOT_ supported.
113              
114             =cut
115              
116             =head1 DESCRIPTION
117              
118             Provides a lightweight persistent filesystem based LRU cache.
119              
120             It uses the 'last accessed' timestamp generated by the file system
121             to determine the 'oldest' cache entry and discards the oldest
122             cache entries when needed to stay under the -keep_last limit.
123              
124             If you store thing very fast (such that many entries receive the
125             same time stamp), it is essentially a coin toss which entry
126             within a single timestamped second gets purged from the cache
127             to make room for new ones.
128              
129             It is not designed to handle huge numbers of cached items. It is probably
130             unwise to set the 'keep_last' higher than around 100.
131              
132             =cut
133              
134             =head1 OBJECT METHODS
135              
136             =cut
137              
138             #######################################################################
139              
140             =over 4
141              
142             =item new({[ -cache_dir => $cache_directory] [, -keep_last => $keep_last_n ] });
143              
144             Creates and optionally initializes a Tie::FileLRUCache object:
145              
146             Example:
147              
148             my $cache = Tie::FileLRUCache->new({
149             -cache_dir => '/tmp/testing',
150             -keep_last => 100,
151             });
152              
153             The default cache size is 100 entries unless specified.
154              
155             =back
156              
157             =cut
158              
159             sub new {
160 13     13 1 4692 my $proto = shift;
161 13         168 my $package = __PACKAGE__;
162 13   66     73 my $class = ref ($proto) || $proto || $package;
163 13         27 my $self = bless {}, $class;
164              
165 13         125 my $parms = parse_parms({ -parms => \@_,
166             -legal => [-cache_dir, -keep_last],
167             -required => [],
168             -defaults => { -keep_last => 100,
169             -cache_dir => undef,
170             },
171             });
172 13 100       1184 if (not defined $parms) {
173 1         6 my $error_message = Class::ParmList->error;
174 1         9 require Carp;
175 1         147 Carp::croak ($package . "::new() - Parameter error '$error_message'\n");
176             }
177              
178             # Save settings
179 12         50 my ($cache_dir,$keep_last) = $parms->get(-cache_dir,-keep_last);
180 12         282 $self->cache_dir($cache_dir);
181 12         25 $self->keep_last($keep_last);
182              
183 12         63 $self;
184             }
185              
186             #######################################################################
187              
188             =over 4
189              
190             =item check({ -key => $key });
191              
192             Reads the cache for the key.
193              
194             Returns two values: $cache_hit (true if a hit was found, false if not)
195             $value (the cached value, undef if no hit)
196              
197             Examples:
198              
199             my ($cache_hit,$value) = $cache->check({ -key => $key });
200              
201             my ($cache_hit,$value) = $cache->check({ -cache_key => $cache_key });
202              
203             The '-key' form is used when you just want to use a raw key. It can use
204             blessed objects, hash refs, scalars, or array refs as keys. The more complex
205             structures take a speed penalty for computing a canonical form.
206             You can minimize this penalty by using the '-cache_key' form instead.
207              
208             The '-cache_key' form is used for performance reasons when using keys
209             such as complex blessed objects or hashes as a key. The -cache_key
210             is obtained with a call to 'make_cache_key'. It is legal to mix
211             -cache_key and -key based calls - they are cross-compatible.
212              
213             =back
214              
215             =cut
216              
217             sub check {
218 48     48 1 554 my $self = shift;
219 48         80 my $package = __PACKAGE__;
220              
221 48 100       122 if (not wantarray) {
222 1         4 require Carp;
223 1         75 Carp::croak ($package . "::check() - Called in a scalar context\n");
224             }
225              
226 47         507 my $parms = parse_parms({ -parms => \@_,
227             -legal => [-cache_key, -key],
228             -required => [],
229             -defaults => {},
230             });
231 46 100       8398 if (not defined $parms) {
232 1         4 my $error_message = Class::ParmList->error;
233 1         7 require Carp;
234 1         97 Carp::croak ($package . "::check() - $error_message\n");
235             }
236 45         7387 my ($key,$cache_key) = $parms->get(-key,-cache_key);
237 45 100 100     1538 if (not (defined ($key) or defined ($cache_key))) {
238 1         3 require Carp;
239 1         79 Carp::croak ($package . "::check() - Called without either a -key or -cache_key\n");
240             }
241              
242 44         147 my $cache_dir = $self->cache_dir;
243 44 100       117 unless (defined $cache_dir) {
244 1         4 require Carp;
245 1         80 Carp::croak ($package . "::check - No cache directory set.\n");
246             }
247              
248             # Ok. Set our lock on the cache
249 43         115 $self->_lock_cache;
250              
251             # Generate the cache_key (done by making a cannonical
252             # network order Storable string out of the key) if we
253             # don't already have it
254 43 100       163 unless (defined $cache_key) {
255 5         25 $cache_key = $self->make_cache_key({ -key => $key });
256             }
257              
258             # Generate a unique cache file name by taking a SHA1 hash of $cache_key
259 43         416 my $cache_hash = lc(sha1_hex($cache_key));
260 43         189 $cache_hash =~ s/\s//gs;
261 43         459 my ($untainted_cache_hash) = $cache_hash =~ m/^([a-f0-9]+)$/s;
262 43         674 my $cache_file = File::Spec->catfile($cache_dir, "cl_$untainted_cache_hash");
263              
264             # Check if there is a cache entry for this key
265 43 100       1580 unless (-e $cache_file) {
266 22         86 $self->_unlock_cache;
267 22         308 return (0,undef);
268             }
269              
270             # Yes. Get it. And update the last modified and last accessed dates.
271 21         31 my $entry;
272 21         51 eval {
273 21         91 $entry = retrieve($cache_file);
274 21         2073 my $now = time;
275 21         1346 utime ($now, $now, $cache_file);
276             };
277 21 50       69 if ($@) {
278 0         0 my $error = $@;
279 0         0 $self->_unlock_cache;
280 0         0 require Carp;
281 0         0 Carp::croak($package . "::check - Error while retrieving cache entry file '$cache_file': $error\n");
282             }
283 21 50       51 unless (defined $entry) {
284 0         0 my $error = $!;
285 0         0 $self->_unlock_cache;
286 0         0 require Carp;
287 0         0 Carp::croak($package . "::update - Failed to retrieve cache entry file '$cache_file': $error\n");
288             }
289              
290             # Release the lock.
291 21         69 $self->_unlock_cache;
292              
293 21         56 my $cache_value = $entry->{'-value'};
294              
295             # Give them their cupie doll
296 21         320 return (1, $cache_value);
297             }
298              
299             #######################################################################
300              
301             =over 4
302              
303             =item make_cache_key({ -key => $key });
304              
305             Generates a cache key by canonicalizing a passed
306             key as a network ordered canonical Storable string.
307              
308             Example:
309              
310             my $cache_key = $cache->make_cache_key({ -key => $key });
311              
312             =back
313              
314             =cut
315              
316             sub make_cache_key {
317 11     11 1 28 my $self = shift;
318 11         21 my $package = __PACKAGE__;
319 11         81 my $parms = parse_parms({ -parms => \@_,
320             -legal => [],
321             -required => ['-key'],
322             -defaults => {},
323             });
324 11 100       933 unless (defined $parms) {
325 1         8 my $error_message = Class::ParmList->error;
326 1         9 require Carp;
327 1         130 Carp::croak ($package . "::make_cache_key() - $error_message\n");
328             }
329 10         37 my ($key) = $parms->get(-key);
330              
331 10         260 my $temp = $Storable::canonical;
332 10         35 my $result = nfreeze(\$key);
333 10         397 $Storable::canonical = $temp;
334              
335 10 50       25 if (not $result) {
336 0         0 my $error = $!;
337 0         0 require Carp;
338 0         0 Carp::croak ($package . "::check() - Unable to serialize passed -key value: $error");
339             }
340 10         54 return $result;
341             }
342              
343             #######################################################################
344              
345             =over 4
346              
347             =item clear;
348              
349             Completely clears the cache of all cache entries.
350              
351             =back
352              
353             =cut
354              
355             sub clear {
356 4     4 1 28 my $self = shift;
357 4         9 my $package = __PACKAGE__;
358 4         13 my $cache_dir = $self->cache_dir;
359              
360 4 100       15 unless (defined $cache_dir) {
361 1         12 require Carp;
362 1         163 Carp::croak ($package . "::clear - No cache directory set.\n");
363             }
364 3 100       9 if ($cache_dir eq '') {
365 1         5 require Carp;
366 1         80 Carp::croak ($package . "::clear - Cannot use root directory as cache directory.\n");
367             }
368 2 50 33     58 if ((-e $cache_dir) and (not -d _)) {
369 0         0 require Carp;
370 0         0 Carp::croak ($package . "::clear - '$cache_dir' already exists and is not a directory.\n");
371             }
372              
373 2         8 $self->_lock_cache;
374              
375 2         11 my $cache_dir_fh = gensym;
376 2 50       135 if (not opendir ($cache_dir_fh, $cache_dir)) {
377 0         0 my $error = $!;
378 0         0 $self->_unlock_cache;
379 0         0 require Carp;
380 0         0 Carp::croak ($package . "::clear - Failed to open directory '$cache_dir' for reading: $error\n");
381             }
382              
383 2         48 my @raw_directory_list = readdir($cache_dir_fh);
384 2 50       38 unless (closedir ($cache_dir_fh)) {
385 0         0 my $error = $!;
386 0         0 $self->_unlock_cache;
387 0         0 require Carp;
388 0         0 Carp::croak ($package . "::clear - Failed to close directory '$cache_dir': $error\n");
389             }
390              
391             # Untaint the filenames, convert them to absolute file paths and unlink them.
392 2         41 my @raw_files_list = grep(/^(cacheline_[a-zA-Z0-9]{1,50}|cl_[a-zA-Z0-9]{1,50})$/s, @raw_directory_list);
393 2         4 my @file_list = ();
394 2         6 foreach my $item (@raw_files_list) {
395 10         73 my ($filename) = $item =~ m/^(.*)$/s;
396 10         138 my $file_path = File::Spec->catfile($cache_dir, $filename);
397 10 50       5379 unless (unlink $file_path) {
398 0         0 my $error = $!;
399 0         0 $self->_unlock_cache;
400 0         0 require Carp;
401 0         0 Carp::croak($package . "::clear - Failed to unlink file '$file_path': $error");
402             }
403             }
404              
405 2         16 $self->_unlock_cache;
406              
407 2         26 return;
408             }
409              
410             #######################################################################
411              
412             =over 4
413              
414             =item update({ [-key => $key,] [-cache_key => $cache_key, ], -value => $value [, -keep_last => $keep_last_n ] });
415              
416             Updates the Least Recently Used (LRU) cache for the specified
417             key with the passed value. '-keep_last' is optional after the first access
418             to a dataset. It will use the I 'keep_last' used
419             if not specified.
420              
421             It is legal to use ordinary scalars, hash references, or array references
422             as keys as well as objects as -keys or -values. Basically, anything that
423             Storable can reproducibly serialize can be used.
424              
425             Examples:
426              
427             $cache->update({ -key => $key, -value => $value });
428              
429             $cache->update({ -key => $key, -value => $value, -keep_last => 100});
430              
431             my $cache_key = $cache->make_cache_key({ -key => $key });
432             $cache->update({ -cache_key => $cache_key, -value => $value });
433              
434             my $cache_key = $cache->make_cache_key({ -key => $key });
435             $cache->update({ -cache_key => $cache_key, -value => $value, -keep_last => 50 });
436              
437             -cache_key is assumed to be a simple scalar value for use as a key.
438              
439             -key can be pretty much anything Storable can successfully and reproducibly serialize.
440              
441             One or the other I be passed.
442              
443             =back
444              
445             =cut
446              
447             sub update {
448 21     21 1 6001245 my $self = shift;
449 21         37 my $package = __PACKAGE__;
450              
451 21         242 my $parms = parse_parms({ -parms => \@_,
452             -legal => ['-keep_last', '-key', '-cache_key'],
453             -required => ['-value'],
454             -defaults => {'-keep_last' => $self->keep_last},
455             });
456 21 100       3259 unless (defined $parms) {
457 1         5 my $error_message = Class::ParmList->error;
458 1         7 require Carp;
459 1         110 Carp::croak ($package . "::update() - $error_message\n");
460             }
461 20         138 my ($key,$cache_key,$value,$keep_last) = $parms->get('-key', '-cache_key', '-value', '-keep_last');
462 20 100 100     1792 unless (defined ($key) or defined ($cache_key)) {
463 1         4 require Carp;
464 1         106 Carp::croak ($package . "::update() - Called without either a -key or -cache_key. At least one of them must be passed.\n");
465             }
466              
467 19         241 my ($cache_dir) = $self->cache_dir;
468 19 100       67 unless (defined $cache_dir) {
469 1         4 require Carp;
470 1         90 Carp::croak ($package . "::update - No cache directory set.\n");
471             }
472              
473             # Generate the cache_key (done by making a cannonical
474             # network order Storable string out of the key) if we
475             # don't already have one.
476 18 100       60 unless (defined $cache_key) {
477 2         9 $cache_key = $self->make_cache_key({ -key => $key });
478             }
479              
480             # Generate a unique cache file
481             # name by taking a SHA1 hash of
482             # $cache_key and dumping it as hex
483 18         203 my $cache_hash = lc(sha1_hex($cache_key));
484 18         2798 $cache_hash =~ s/\s//gs;
485 18         165 my ($untainted_cache_hash) = $cache_hash =~ m/^([a-f0-9]+)$/s;
486 18         382 my $cache_file = File::Spec->catfile($cache_dir, "cl_$untainted_cache_hash");
487              
488             # Serialize the $value for storage
489 18         81 my $entry = { -value => $value };
490              
491             # Set our lock on the cache directory
492 18         1560 $self->_lock_cache;
493              
494             ##########
495             # Store the cache entry.
496 18         27 my $result;
497 18         56 eval { $result = nstore($entry,$cache_file); };
  18         115  
498 18 50       7207 if ($@) {
499 0         0 my $error = $@;
500 0         0 $self->_unlock_cache;
501 0         0 require Carp;
502 0         0 Carp::croak($package . "::update - Error while saving cache entry file '$cache_file': $error");
503             }
504 18 50       66 unless (defined $result) {
505 0         0 my $error = $!;
506 0         0 $self->_unlock_cache;
507 0         0 require Carp;
508 0         0 Carp::croak($package . "::update - Error while saving cache entry file '$cache_file': $error\n");
509             }
510              
511             ########################################
512             # Check if we need to purge old entries
513 18         74 my $cache_dir_fh = gensym;
514 18 50       1349 unless (opendir ($cache_dir_fh, $cache_dir)) {
515 0         0 my $error = $!;
516 0         0 $self->_unlock_cache;
517 0         0 require Carp;
518 0         0 Carp::croak ($package . "::update - Failed to open directory '$cache_dir' for reading: $error\n");
519             }
520 18         2149 my @raw_file_list = grep(/^(cacheline_[a-fA-F0-9]{1,50}|cl_[a-fA-F0-9]{1,50})$/s,readdir($cache_dir_fh));
521 18 50       427 unless (closedir ($cache_dir_fh)) {
522 0         0 my $error = $!;
523 0         0 $self->_unlock_cache;
524 0         0 require Carp;
525 0         0 Carp::croak ($package . "::update - Failed to close directory '$cache_dir': $error\n");
526             }
527              
528             # Untainting the filenames and converting them to absolute file paths.
529 18         44 my @file_list = ();
530 18         56 foreach my $item (@raw_file_list) {
531 58         222 my ($filename) = $item =~ m/^(.*)$/s;
532 58         625 my $file_path = File::Spec->catfile($cache_dir, $filename);
533 58         182 push (@file_list,$file_path);
534             }
535 18         55 my $n_files = $#file_list + 1;
536              
537             # No problems. All done.
538 18 100       51 if ($n_files <= $keep_last) {
539 14         57 $self->_unlock_cache;
540 14         687 return;
541             }
542              
543             # Too many entries. Delete the excess entries (usually only one)
544 4         15 my %file_last_access = ();
545 4         11 foreach my $file (@file_list) {
546 24         493 my $last_accessed = (stat($file))[9];
547 24         89 $file_last_access{$file} = $last_accessed;
548             }
549 4         36 my @sorted_file_list = sort { $file_last_access{$b} <=> $file_last_access{$a} } @file_list;
  40         96  
550 4   66     34 while (($n_files > $keep_last) and ($n_files > 0)) {
551 4         7 $n_files--;
552 4         9 my $pruned_file = $sorted_file_list[$n_files];
553 4 50       126818 unless (unlink $pruned_file) {
554 0         0 my $error = $!;
555 0         0 $self->_unlock_cache;
556 0         0 require Carp;
557 0         0 Carp::croak($package . "::update - Failed to unlink file '$pruned_file': $error");
558             }
559             }
560              
561             # Release our lock and return
562 4         169 $self->_unlock_cache;
563 4         111 return;
564             }
565              
566             #######################################################################
567              
568             =over 4
569              
570             =item delete({ -key => $key });
571              
572             Forces the deletion of a specific key from the cache.
573              
574             Example:
575              
576             $cache->delete({ -key => $key });
577              
578             =back
579              
580             =cut
581              
582             sub delete {
583 8     8 1 86 my $self = shift;
584 8         10 my $package = __PACKAGE__;
585              
586 8         60 my $parms = parse_parms({ -parms => \@_,
587             -legal => [-key, -cache_key],
588             -required => [],
589             -defaults => {},
590             });
591 7 100       529 if (not defined $parms) {
592 1         4 my $error_message = Class::ParmList->error;
593 1         6 require Carp;
594 1         97 Carp::croak ($package . "::delete() - $error_message\n");
595             }
596 6         28 my ($key,$cache_key) = $parms->get(-key, -cache_key);
597 6 100 100     149 if (not (defined ($key) or defined ($cache_key))) {
598 1         4 require Carp;
599 1         75 Carp::croak ($package . "::delete() - Called without either a -key or -cache_key\n");
600             }
601              
602 5         12 my $cache_dir = $self->cache_dir;
603 5 100       13 unless (defined $cache_dir) {
604 1         25 require Carp;
605 1         82 Carp::croak ($package . "::delete - No cache directory set.\n");
606             }
607 4 50       12 if ($cache_dir eq '') {
608 0         0 require Carp;
609 0         0 Carp::croak ($package . "::delete - Cannot use root directory as cache directory.\n");
610             }
611              
612             # Generate the cache_key (done by making a cannonical
613             # network order Storable string out of the key) if we
614             # don't already have it
615 4 100       11 if (not defined $cache_key) {
616 2         8 $cache_key = $self->make_cache_key({ -key => $key });
617             }
618              
619             # Generate a unique cache file
620             # name by taking a SHA1 hash of
621             # $cache_key
622 4         21 my $cache_hash = lc(sha1_hex($cache_key));
623 4         11 $cache_hash =~ s/\s//gs;
624 4         19 my ($untainted_cache_hash) = $cache_hash =~ m/^([a-f0-9]+)$/s;
625 4         52 my $cache_file = File::Spec->catfile($cache_dir, "cl_$untainted_cache_hash");
626              
627             # Ok. Set our lock on the cache directory
628 4         14 $self->_lock_cache;
629              
630             # If it is in the cache, remove it
631 4 50 33     446 if ((-e $cache_file) and (not unlink $cache_file)) {
632 0         0 my $error = $!;
633 0         0 $self->_unlock_cache;
634 0         0 require Carp;
635 0         0 Carp::croak($package . "::delete - Failed to unlink file '$cache_file': $error");
636             }
637              
638             # Release our lock and return
639 4         14 $self->_unlock_cache;
640             }
641              
642             #######################################################################
643              
644             =over 4
645              
646             =item cache_dir([$cache_directory_path]);
647              
648             Get/Set accessor for the cache directory path.
649              
650             Ex.
651              
652             my $cache_directory = $cache->cache_dir;
653              
654             $cache->cache_dir($cache_directory);
655              
656             =back
657              
658             =cut
659              
660 163     163 1 12950 sub cache_dir { return shift->_property('cache_dir', @_); }
661              
662             #######################################################################
663              
664             =over 4
665              
666             =item keep_last([$keep_last_n]);
667              
668             Get/Set accessor for the keep last N setting.
669              
670             Ex.
671              
672             my $n_last = $cache->keep_last;
673              
674             $cache->keep_last(20);
675              
676             =back
677              
678             =cut
679              
680 37     37 1 124 sub keep_last { return shift->_property('keep_last', @_); }
681              
682              
683             #######################################################################
684              
685             =over 4
686              
687             =item number_of_entries;
688              
689             Returns the current number of entries in the cache.
690              
691             =back
692              
693             =cut
694              
695             sub number_of_entries {
696 2     2 1 2000337 my $self = shift;
697 2         8 my $package = __PACKAGE__;
698              
699 2         23 my $cache_dir_fh = gensym;
700 2         64 my $cache_dir = $self->cache_dir;
701 2 50       15 unless (defined $cache_dir) {
702 0         0 require Carp;
703 0         0 Carp::croak ($package . "::delete - No cache directory set.\n");
704             }
705 2 50       12 if ($cache_dir eq '') {
706 0         0 require Carp;
707 0         0 Carp::croak ($package . "::delete - Cannot use root directory as cache directory.\n");
708             }
709              
710 2 50       156 unless (opendir ($cache_dir_fh, $cache_dir)) {
711 0         0 my $error = $!;
712 0         0 require Carp;
713 0         0 Carp::croak ($package . "::update - Failed to open directory '$cache_dir' for reading: $error\n");
714             }
715 2         171 my @raw_file_list = grep(/^(cacheline_[a-fA-F0-9]{1,50}|cl_[a-fA-F0-9]{1,50})$/s,readdir($cache_dir_fh));
716 2 50       134 unless (closedir ($cache_dir_fh)) {
717 0         0 my $error = $!;
718 0         0 require Carp;
719 0         0 Carp::croak ($package . "::update - Failed to close directory '$cache_dir': $error\n");
720             }
721 2         8 my $n_entries = $#raw_file_list + 1;
722 2         21 return $n_entries;
723             }
724              
725             #######################################################################
726             # #
727             # PRIVATE METHODS #
728             # #
729             # Internals. Documented for maintainance reasons only. #
730             # Do not use these methods from outside this module. #
731             # #
732             #######################################################################
733              
734             #######################################################################
735             # _cache_lock_fh([$fh]);
736             #
737             # Get/Set accessor used to store a reference to the filehandle
738             # used for locking.
739              
740 134     134   400 sub _cache_lock_fh { return shift->_property('_cache_lock_fh', @_); }
741              
742             #######################################################################
743             # _lock_cache;
744             #
745             # Obtains a lock on the 'cache.lock' file for this LRU cache.
746             #
747             # Example:
748             # $self->_lock_cache;
749             #
750             # This will create the 'cache.lock' file if it does not already exist,
751             # creating any intermediate directories as needed.
752             #
753             # It also writes the current PID to the lock file.
754              
755             sub _lock_cache {
756 67     67   114 my $self = shift;
757 67         95 my $package = __PACKAGE__;
758              
759 67         171 my $cache_dir = $self->cache_dir;
760 67 50       3238 if (not defined $cache_dir) {
761 0         0 require Carp;
762 0         0 Carp::croak ($package . "::_lock_cache - No cache directory set.\n");
763             }
764 67 50       5420 if ($cache_dir eq '') {
765 0         0 require Carp;
766 0         0 Carp::croak ($package . "::_lock_cache - Cannot use root directory as cache directory.\n");
767             }
768 67 50 33     7122 if ((-e $cache_dir) and (not -d _)) {
769 0         0 require Carp;
770 0         0 Carp::croak ($package . "::_lock_cache - '$cache_dir' already exists and is not a directory.\n");
771             }
772 67 50       1689 if (not -e $cache_dir) {
773 0         0 eval {
774 0         0 require File::Path;
775 0         0 File::Path::mkpath ($cache_dir);
776             };
777 0 0       0 if ($@) {
778 0         0 my $error = $@;
779 0         0 require Carp;
780 0         0 Carp::croak ($package . "::_lock_cache - unable to create directory '$cache_dir': $error");
781             }
782             }
783 67 50 33     2073 if (not ((-e $cache_dir) and (-d _))) {
784 0         0 require Carp;
785 0         0 Carp::croak ($package . "::_lock_cache - Unable to create directory '$cache_dir'\n");
786             }
787 67         1296 my $document_name = File::Spec->catfile($cache_dir,'.cache.lock');
788 67         375 my $cache_lock_fh = gensym;
789 67 50       14474 unless (open ($cache_lock_fh,">>$document_name")) {
790 0         0 my $error = $!;
791 0         0 require Carp;
792 0         0 Carp::croak ($package . "::_lock_cache - Unable to open '$document_name': $error\n");
793             }
794 67         154 my $lock_timeout = 100;
795 67         686 while (not flock($cache_lock_fh, LOCK_EX()|LOCK_NB())) {
796 0         0 $lock_timeout--;
797 0         0 select (undef,undef,undef,0.1);
798 0 0       0 if ($lock_timeout == 0) {
799 0         0 my $error = $!;
800 0         0 require Carp;
801 0         0 Carp::croak ($package . "::_lock_cache - Unable to get an exclusive lock on '$document_name': $error\n");
802             }
803             }
804 67         289 my $fh = select ($cache_lock_fh);
805 67         9665 $|++;
806 67         214 select ($fh);
807 67 50       1063084 unless (truncate ($cache_lock_fh, 0)) {
808 0         0 my $error = $!;
809 0         0 require Carp;
810 0         0 Carp::croak ($package . "::_lock_cache - Unable to truncate '$document_name': $error\n");
811             }
812 67         3356146 print $cache_lock_fh "$$\n";
813 67         354 $self->_cache_lock_fh($cache_lock_fh);
814              
815 67         520 return;
816             }
817              
818             #######################################################################
819              
820             # _unlock_cache;
821             #
822             # Release a lock on the 'cache.lock' file for this LRU cache.
823             #
824             # Example:
825             # $self->_unlock_cache;
826              
827             sub _unlock_cache {
828 67     67   119 my $self = shift;
829 67         128 my $package = __PACKAGE__;
830              
831 67         176 my $cache_lock_fh = $self->_cache_lock_fh;
832 67 50       4013798 unless (truncate ($cache_lock_fh,0)) {
833 0         0 my $error = $!;
834 0         0 require Carp;
835 0         0 Carp::croak ($package . "::_lock_cache - Unable to truncate cache.lock file: $error\n");
836             }
837 67 50       8148 unless (close ($cache_lock_fh)) {
838 0         0 my $error = $!;
839 0         0 require Carp;
840 0         0 Carp::croak ($package . "::_unlock_cache - Error while closing cache.lock file: $error\n");
841             }
842 67         224 return;
843             }
844              
845             ####################################################################
846             # _property('property_name' => $property_value)
847             #
848             # get/set base accessor for property values
849              
850             sub _property {
851 334     334   472 my $self = shift;
852              
853 334         1285 my $property = shift;
854              
855 334         1779 my $package = __PACKAGE__;
856 334 100       829 if (0 == @_) {
    50          
857 229         537 my $output = $self->{$package}->{$property};
858 229         1878 return $output;
859              
860             } elsif (1 == @_) {
861 105         149 my $input = shift;
862 105         316 $self->{$package}->{$property} = $input;
863 105         1039 return;
864              
865             } else {
866 0         0 require Carp;
867 0         0 Carp::croak("Bad calling parameters to ${package}::${property}()\n");
868             }
869             }
870              
871             ####################################################################
872              
873             sub TIEHASH {
874 4     4   5641 my $proto = shift;
875 4         7 my $package = __PACKAGE__;
876 4   33     42 my $class = ref ($proto) || $proto || $package;
877 4         10 my $self = bless {}, $class;
878              
879 4         9 my ($cache_dir,$keep_last) = @_;
880              
881 4 100       38 $keep_last = 100 unless (defined $keep_last);
882 4 100 66     21 unless (defined ($cache_dir) and ($cache_dir ne '')) {
883 1         14 require Carp;
884 1         684 Carp::croak($package . ": Missing required parameter (cache_dir)\n");
885             }
886 3         9 $self->cache_dir($cache_dir);
887 3         9 $self->keep_last($keep_last);
888 3         11 return $self;
889             }
890              
891             #######################################################################
892              
893             sub STORE {
894 9     9   6006297 my $self = shift;
895              
896 9         42 my ($key,$value) = @_;
897              
898 9 100       76 if (ref(\$key) eq 'SCALAR') {
899 8         100 $self->update({ -cache_key => $key, -value => $value });
900             } else {
901 1         6 $self->update({ -key => $key, -value => $value });
902             }
903             }
904              
905             #######################################################################
906              
907             sub FETCH {
908 7     7   64 my $self = shift;
909              
910 7         19 my ($key) = @_;
911              
912 7 100       28 if (ref(\$key) eq 'SCALAR') {
913 6         33 my ($cache_hit, $value) = $self->check({ -cache_key => $key });
914 6         44 return $value;
915              
916             } else {
917 1         22 my ($cache_hit,$value) = $self->check({ -key => $key });
918 1         10 return $value;
919             }
920              
921             }
922              
923             #######################################################################
924              
925             sub DELETE {
926 2     2   26 my $self = shift;
927              
928 2         5 my ($key) = @_;
929              
930 2 100       10 if (ref(\$key) eq 'SCALAR') {
931 1         9 $self->delete({ -cache_key => $key });
932             } else {
933 1         7 $self->delete({ -key => $key });
934             }
935             }
936              
937             #######################################################################
938              
939             sub CLEAR {
940 1     1   9 my $self = shift;
941              
942 1         6 $self->clear;
943             }
944              
945             #######################################################################
946              
947             sub EXISTS {
948 18     18   207 my $self = shift;
949              
950 18         39 my ($key) = @_;
951              
952 18 100       79 if (ref(\$key) eq 'SCALAR') {
953 16         92 my ($cache_hit,$value) = $self->check({ -cache_key => $key });
954 16         92 return $cache_hit;
955             } else {
956 2         12 my ($cache_hit,$value) = $self->check({ -key => $key });
957 2         11 return $cache_hit;
958             }
959             }
960              
961             #######################################################################
962              
963             #
964             # Iteration over the cache is not supported
965             #
966              
967 1     1   20 sub FIRSTKEY { undef; }
968              
969             #######################################################################
970              
971             #
972             # Iteration over the cache is not supported
973             #
974              
975 0     0     sub NEXTKEY { undef; }
976              
977             #######################################################################
978             #
979             # We return the number of cache entries in a scalar context
980             #
981              
982             sub SCALAR {
983 0     0     my $self = shift;
984              
985 0           return $self->number_of_entries;
986             }
987              
988             #######################################################################
989             #######################################################################
990              
991             =head1 COPYRIGHT
992              
993             Copyright 1999, Benjamin Franz () and
994             FreeRun Technologies, Inc. (). All Rights Reserved.
995              
996             =head1 VERSION
997              
998             1.05 released 2005.09.14
999              
1000             =head1 LICENSE
1001              
1002             This software may be copied or redistributed under the same terms as Perl itelf.
1003              
1004             This means that you can, at your option, redistribute it and/or modify it under
1005             either the terms the GNU Public License (GPL) version 1 or later, or under the
1006             Perl Artistic License.
1007              
1008             See http://dev.perl.org/licenses/
1009              
1010             =head1 DISCLAIMER
1011              
1012             THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS
1013             OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
1014             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
1015             PARTICULAR PURPOSE.
1016              
1017             Use of this software in any way or in any form, source or binary,
1018             is not allowed in any country which prohibits disclaimers of any
1019             implied warranties of merchantability or fitness for a particular
1020             purpose or any disclaimers of a similar nature.
1021              
1022             IN NO EVENT SHALL I BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
1023             SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
1024             USE OF THIS SOFTWARE AND ITS DOCUMENTATION (INCLUDING, BUT NOT
1025             LIMITED TO, LOST PROFITS) EVEN IF I HAVE BEEN ADVISED OF THE
1026             POSSIBILITY OF SUCH DAMAGE
1027              
1028             =head1 AUTHOR
1029              
1030             Benjamin Franz
1031              
1032             =head1 TODO
1033              
1034             Nothing.
1035              
1036             =cut
1037              
1038             1;