File Coverage

blib/lib/Cache/RamDisk.pm
Criterion Covered Total %
statement 83 265 31.3
branch 14 86 16.2
condition 14 50 28.0
subroutine 18 21 85.7
pod 8 8 100.0
total 137 430 31.8


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Cache::RamDisk
4              
5             Sharing of Perl Objects Between Processes on Several RAM Drives
6              
7             =head1 VERSION
8              
9             0.1.6
10              
11             =head1 SYNOPSYS
12              
13             Application start phase:
14              
15             use Cache::RamDisk::Functions;
16              
17             cache_install( { 'Base' => '/tmp/rd',
18             'Size' => 16,
19             'INodes' => 1024,
20             'SIndex' => { 'fie' => 8,
21             'foe' => 64,
22             'fum' => 512 },
23             'ShMem' => 'RdLk',
24             'Keys' => { 'fie' => 50,
25             'foe' => 200,
26             'fum' => 4000 },
27             'User' => 'apache',
28             'Group' => 'apache' } );
29              
30             Content handler code:
31              
32             use MyApp::Fie;
33              
34             my $fie = MyApp::Fie->new (12345);
35             print $fie->{'some_field'};
36              
37              
38             Object code:
39              
40             package MyApp::Fie;
41             use Cache::RamDisk;
42              
43             sub new {
44             my ($class, $id) = @_;
45             my $c = Cache::RamDisk->new ('/tmp/rd', CACHE_LRU);
46             my $self = $c->get({'fie' => $id})->{'fie'}->{$id} || do {
47             # perform some db logics
48             $self = $sth->fetchrow_hashref;
49             bless $self, $class;
50             $c->put({'fie' => { $id => $self } });
51             }
52             $self;
53             }
54              
55              
56             Later on in a cgi script:
57              
58             use CGI qw(:html);
59             use Cache::RamDisk::Functions;
60             [...]
61             my $s = cache_status ('/tmp/rd');
62             [...]
63             print "Number of items for 'fie': ".$s->key_stat('fie'), br;
64              
65              
66             On application shutdown:
67              
68             cache_remove ('/tmp/rd');
69              
70              
71             =head1 DESCRIPTION
72              
73             I
74             to write or exclaim that herewith specified thing.>
75              
76             Cache::RamDisk provides multi-process applications with a means of sharing Perl objects between the
77             processes while trying to avoid the inconveniences inherent to other IPC tools:
78              
79             1. Message queues are extremely fast, but extremely limited too.
80              
81             2. Shared memory is perhaps even faster, but it came out for me to be an at least hairy problem
82             trying to store several references all in one segment.
83              
84             3. Sockets are reliable, but require a second communication endpoint and yet another server process.
85              
86             But a file is a file is a file.
87              
88             The package collects as much ramdisks to a bundle as possible and necessary to hold the required user space,
89             depending on the respective parameters under which the system's individual kernel had been compiled.
90             The system user and group who owns the cache can be specified for the whole rd bunch, say cache.
91              
92              
93             =head2 Cache Types
94              
95             The package provides three ways of cacheing policy. The desired type can be submitted to the individual
96             object constructor with one of the following values:
97              
98             =head3 CACHE_LRU
99              
100             Forces the accessing object methods to treat the cache under Bast Becently Bsed aspects: an existent
101             object will be delivered, and the respective index entry moves to the top. Values from the 'Keys' reference
102             in the latest call to C define the B for the respective key. If the index
103             list is full, its last line will be Cped and the new entry Ced.
104              
105             =head3 CACHE_TIMED
106              
107             All accesses from this object to the cache treat the value for each cache key as B
108             that cache objects belonging to the key are allowed to reach. "Stale" objects will not be delivered,
109             but removed instead. (The decision whether to deliver or remove happens on every C request. There
110             may a thread be born some day.)
111              
112             =head3 CACHE_DEFAULT
113              
114             A fallback policy by which you may use parts of your cache as a convenient substitute for SysV shared
115             memory. Values set for the cache keys are ignored - which means that you will have
116             to invalidate objects on this type of cache "by hand". Indexes are being kept up to date by simple
117             Cs and Cs. B
118              
119              
120             =head1 REQUIRES
121              
122             Perl B<5.6.1> on a Linux/ Unix System containing the following binaries:
123              
124             chown, mkdir, mke2fs, mount, umount
125              
126             The package uses these Perl modules available from CPAN:
127              
128             IPC::Shareable
129             IPC::SysV
130             Filesys::Df
131             Filesys::Statvfs
132             File::stat
133             Fcntl
134             Symbol
135             Class::Struct
136             POSIX
137              
138              
139             =head1 EXPORTS
140              
141             CACHE_DEFAULT, CACHE_LRU, CACHE_TIMED
142              
143             =head1 METHODS
144              
145             =head2 Class Methods
146              
147             =head3 Cache::RamDisk->new ( $basepath [, $type [, $shmemkey]])
148              
149             Creates a new class instance that will act on the cache denoted by C<$basepath>. If no C<$type> has
150             been requested, C is assumed. Please note that, although it is possible to access one cache
151             with several types of instances at the same time, it is not intended to create one instance of
152             several types. Do not try to perform bitwise operations on C<$type>, as their results will all lead to a
153             C cache. Returns undef when called as a class method, or when the 'Base' parameter is
154             missing. The C<$shmemkey> argument is optional, but it becomes a vital necessity, if you want to access
155             a cache that has been created with another than the default key. See L for
156             details.
157              
158             From this and from what can be read under L follows: it is up to the programmer's
159             responsibility how the cache will act. You will always have to have in mind which keys on the cache
160             you want to be treated like what in one application. If you don't always stick to the same type of
161             cache for one key, you will most likely mostly never get a predictable result, but see far below.
162              
163              
164             =head2 Object Methods
165              
166             All of the following methods return the C value when called with faulty arguments, unless
167             no runtime error prevents them from returning at all. ("faulty" means that they return the undef value
168             also when called as class methods.)
169              
170             =head3 $c-Eerrstr
171              
172             If an error has ocurred in the current B, holds the message.
173              
174             =head3 $c-Eget ( $href )
175              
176             Get one or more objects from the current cache. The values in C<$href> may be scalars or arrayrefs.
177             There may obviously be more than one key in the argument hash. Thus C is a means to retrieve
178             a bunch of objects at one request. Returns a reference to a hash of hashrefs.
179              
180             Examples:
181              
182             $c->get( { 'fie' => 12345 } ) returns { 'fie' => { '12345' => $a_reference }}
183              
184             $c->get( { 'fie' => [12345, 67890],
185             'fum' => 54321 } )
186             returns { 'fie' => { '12345' => $a_reference,
187             '67890' => $another_reference },
188             'fum' => { '54321' => $something_blessed } }
189              
190             If an object could not be found on the cache the respective value will be 0. If a non-existing key
191             has been submitted, the respective value will be C. Returns C and sets C if no argument
192             can be found, or the argument isn't a hashref, or an OS error occurs.
193              
194              
195             =head3 $c-Eput ( $href )
196              
197             Puts objects on the cache. The hashref it requires has to look like what C returns.
198             How C behaves depends on which type of cache you chose. Returns 1 on success, or undef else and
199             sets the by C accessible field.
200             Requests for keys which do not exist on the cache are ignored. Values being no hashrefs are ignored.
201              
202              
203             =head3 $c-Einvalidate ( $href )
204              
205             Invalidates cache entries. With CACHE_LRU and CACHE_TIMED instances the C method will automatically
206             perform the necessary steps, and mostly you will not need to C something by hand.
207             On a CACHE_DEFAULT cache, however, you will have to remove objects from time to time. Awaits a hashref
208             like C. Returns 1 on success, else sets C and returns C.
209              
210              
211             =head1 LOCKING
212              
213             C operations lock the whole cache by locking the 'ShMem' segment exclusively. C and
214             C operations apply a shared lock to this segment for every input key, except the moments when they
215             have to write back eventually changed data. Whenever a C accesses a key, it locks "its" respective
216             shmem segment exclusively, because every access alters the index order. This means that put()s
217             are egoistic, whereas get()s and invalidations try to be fair.
218              
219              
220             =head1 SOME NOTES
221              
222             If running under Apache all filehandles will be created by C<&Apache::gensym>, else C<&Symbol::gensym> gets
223             called.
224              
225             There is a means of controlling the size of a running cache: it lies in performing a LRU put on a key that
226             elsewhere in your application is being used as TIMED - because a LRU put trims the number of items to the
227             limit a key has to obey.
228              
229             A cache can serve more than one application - as long as they all stick to the same 'ShMem' segment.
230              
231             Attempts to store anything else than a Perl reference will be ignored.
232              
233              
234             =head1 KNOWN BUGS
235              
236             Something seems to go wrong in connection with TIMED gets: getting 10000 objects that have to be
237             declared stale after 4000 seconds resulted even after just under 1000 seconds in a minimization
238             of used disk spaces to 51% (!?) each. And I simply cannot figure out why: the respective codeline
239             looks proper to me.
240              
241              
242             =head1 TODO
243              
244             Implement some sort of debug mode?
245              
246             Signal handling?
247              
248              
249             =head1 SEE ALSO
250              
251             L
252              
253             =for html html link: Cache::RamDisk::Functions
254              
255             =head1 AUTHOR
256              
257             Martin Haase-Thomas ELE
258              
259             =head1 HISTORY
260              
261             B<0.1.6> Nothing here, see Functions.pm
262              
263             B<0.1.5> Some smaller changes due to my newly achieved respect for initrd's.
264              
265             B<0.1.4> added invalidate method, published on thcsoft.de 07/26/02.
266              
267             B<0.1.3> altered 'SIndex' logics, published on thcsoft.de 07/25/02.
268              
269             B<0.1.2> rewrote them again, published on thcsoft.de 07/24/02.
270              
271             B<0.1.1> rewrote object methods to fulfill the locking policy
272              
273             B<0.1> Jul. 02, first approach, runnable under certain conditions
274              
275              
276             =cut
277              
278             ##############################################################################
279              
280             package Cache::RamDisk;
281              
282             # a cache implementation based on ramdisks.
283             # all methods return undef in case of an error and leave an error message that can be retrieved
284             # by $c->errstr
285              
286             require 5.6.1;
287 1     1   1037 use strict;
  1         2  
  1         139  
288 1     1   7 use warnings;
  1         2  
  1         36  
289 1     1   5 no warnings 'untie'; # no idea where all those funny inner references come from when
  1         4  
  1         34  
290 1     1   5 use Exporter; # trying to untie from IPC::Shareable
  1         2  
  1         221  
291              
292 1     1   7 use vars qw(@ISA $VERSION @EXPORT);
  1         1  
  1         243  
293             @ISA = qw(Exporter);
294             @EXPORT = qw(CACHE_DEFAULT CACHE_LRU CACHE_TIMED);
295             $VERSION = 0.1.6;
296              
297 0     0 1 0 sub CACHE_DEFAULT { 0 }
298 4     4 1 54 sub CACHE_LRU { 1 }
299 0     0 1 0 sub CACHE_TIMED { 2 }
300              
301 1     1   1926 use IPC::Shareable qw(:lock);
  1         45531  
  1         217  
302 1     1   2245 use Filesys::Df;
  1         3945  
  1         94  
303 1     1   8666 use Filesys::Statvfs;
  1         1050  
  1         85  
304 1     1   11492 use File::stat;
  1         14127  
  1         145  
305 1     1   101 use Fcntl qw( O_CREAT O_RDONLY O_RDWR );
  1         3  
  1         57  
306 1     1   1406 use Symbol;
  1         1592  
  1         97  
307 1     1   1551 use POSIX 'ceil';
  1         11978  
  1         8  
308              
309             # constructor.
310             # args: Base(path) for the rds, cache type
311             sub new {
312 2   50 2 1 8 my $class = shift || return undef;
313 2 50       7 return undef if ref $class;
314 2         3 my $self = {};
315 2         5 $self->{Err} = 0;
316 2   50     7 $self->{Base} = shift || return undef;
317              
318 2   33     6 my $type = shift || CACHE_DEFAULT; # == 0
319 2 50 33     5 $type = CACHE_DEFAULT unless ($type == CACHE_LRU || $type == CACHE_TIMED);
320 2         5 $self->{Type} = $type;
321              
322 2   50     13 $self->{ShMem} = shift || 'RdLk';
323              
324 2         5 bless $self, $class;
325 2         7 $self;
326             }
327              
328             # returns a hashref of hashrefs for each requested item.
329             # e.g.:
330             # $cache->get( { 'category' => 1201,
331             # 'post' => [ 1111 2222 3333 4444 ] })
332             # returns (assumed all items could be found on the cache):
333             # { 'category' => { '1201' => blessed ref },
334             # 'post' => { '1111' => blessed ref,
335             # '2222' => blessed ref,
336             # ...
337             # }
338             # }
339             sub get {
340 1   50 1 1 19 my $self = shift || return undef;
341 1 50 33     5 return undef unless (ref $self || $self->{Err}); # we have to be in a clean state
342 1         2 $self->{Err} = 0;
343 1         3 my $what = shift;
344 1 50 33     4 unless ($what || ref($what) ne 'HASH') {
345 0         0 $self->errstr("Argument has to be a hashref in call to get");
346 0         0 return undef;
347             }
348              
349 1         2 my $that = {};
350 1         1 my (%cache, $ctie);
351 1 50       3 unless (eval { $ctie = tie %cache, 'IPC::Shareable', $self->{ShMem}, { create => 0, destroy => 0,
  1         8  
352             exclusive => 0, mode => 0666,
353             size => 65536 } } ) {
354 1         363 $self->errstr($@);
355 1         3 return undef;
356             }
357              
358 0         0 my ($k, @key, $xtie, $idx, $id, $rd, $rdpath, $hdl, $stats, $item);
359 0         0 foreach $k (keys %{$what}) {
  0         0  
360 0         0 $ctie->shlock(LOCK_SH);
361 0 0       0 unless (exists $cache{$k}) {
362 0         0 $that->{$k} = undef;
363 0         0 next;
364             }
365 0         0 @key = split /:/, $cache{$k}; # [0] = cache value, [1] = items on cache, [2] = ftok, [3] = shmemsize
366              
367 0 0       0 undef $xtie if $xtie;
368 0 0       0 untie $idx if tied $idx;
369 0 0       0 unless (eval { $xtie = tie $idx, 'IPC::Shareable', $key[2], { create => 0, destroy => 0,
  0         0  
370             exclusive => 0, mode => 0666,
371             size => $key[3]*1024 } } ) {
372 0         0 $self->errstr($@);
373 0         0 undef $ctie;
374 0         0 untie %cache;
375 0         0 return undef;
376             }
377 0 0       0 $what->{$k} = [ $what->{$k} ] unless ref $what->{$k} eq 'ARRAY'; # try to normalize input
378 0         0 $that->{$k} = {};
379 0         0 $xtie->shlock(LOCK_EX);
380 0         0 foreach $id (@{$what->{$k}}) {
  0         0  
381 0         0 $idx =~ s/(\d+)\/\/$id\n//s; # "splice" ...
382 0         0 $key[1]--;
383 0 0 0     0 unless ($& || $1) {
384 0         0 $that->{$k}->{$id} = 0;
385 0         0 next;
386             }
387 0         0 chop ($rdpath = $&);
388 0         0 $rd = $1;
389 0         0 $rdpath =~ s/\/\//\/$k\//;
390 0         0 $rdpath = $self->{Base}.$rdpath;
391 0 0       0 $that->{$k}->{$id} = 0 unless eval { $stats = stat $rdpath };
  0         0  
392              
393             # this is what makes a cache to be a timed cache:
394 0 0 0     0 if ($self->{Type} == CACHE_TIMED && ($stats->atime < (time()-$key[0]))) {
395 0         0 unlink $rdpath;
396 0         0 $that->{$k}->{$id} = 0;
397 0         0 next;
398             }
399              
400 0         0 $hdl = $self->_gensym;
401             sysopen($hdl, $rdpath, O_RDONLY, 0644) ||
402 0 0       0 do { $self->errstr("$rdpath: $!");
  0         0  
403 0         0 $that->{$k}->{$id} = undef;
404 0         0 $ctie->shlock(LOCK_EX);
405 0         0 $cache{$k} = join ":", @key;
406 0         0 $ctie->shunlock;
407 0         0 next;
408             };
409 0         0 sysread $hdl, $item, $stats->size;
410 0         0 close $hdl;
411              
412 0 0       0 $idx = "$rd//$id\n".$idx if $item; # ... and "unshift"
413 0         0 $key[1]++;
414 0 0       0 $that->{$k}->{$id} = ($item ? Storable::thaw($item) : 0);
415             }
416 0         0 $xtie->shunlock;
417 0         0 undef $xtie;
418 0         0 untie $idx;
419              
420 0         0 $ctie->shlock(LOCK_EX);
421 0         0 $cache{$k} = join ":", @key;
422 0         0 $ctie->shlock(LOCK_UN);
423             }
424 0         0 undef $ctie;
425 0         0 untie %cache;
426 0         0 $that;
427             }
428              
429              
430             # requests what get() returns as input. returns 1 on success, else undef.
431             sub put {
432 1   50 1 1 25 my $self = shift || return undef;
433 1 50       6 return undef if $self->{Err};
434 1 50       4 unless (ref $self) {
435 0         0 $self->errstr("You can't call Cache::RamDisk::put as a class method");
436 0         0 return undef;
437             }
438 1         2 $self->{Err} = 0;
439 1         2 my $what = shift;
440 1 50 33     5 unless ($what || ref($what) eq 'HASH') {
441 0         0 $self->errstr("Argument has to be a hashref in call to put");
442 0         0 return undef;
443             }
444              
445 1         2 my (%cache, $ctie);
446 1 50       2 unless (eval { $ctie = tie %cache, 'IPC::Shareable', $self->{ShMem}, { create => 0, destroy => 0,
  1         18  
447             exclusive => 0, mode => 0666,
448             size => 65536 } } ) {
449 1         537 $self->errstr($@);
450 1         3 return undef;
451             }
452              
453 0         0 $ctie->shlock(LOCK_EX);
454 0         0 my ($k, @key, $xtie, $idx, $id, $rd, $rdpath, $df, $item, $l, $hdl, $iline);
455 0         0 foreach $k (keys %{$what}) {
  0         0  
456 0 0       0 next unless exists($cache{$k});
457 0         0 @key = split /:/, $cache{$k}; # [0] = cache value, [1] = items on cache, [2] = ftok, [3] = shmemsize
458              
459 0 0       0 undef $xtie if defined($xtie);
460 0 0       0 untie $idx if tied $idx;
461 0 0       0 unless (eval { $xtie = tie $idx, 'IPC::Shareable', $key[2], { create => 0, destroy => 0,
  0         0  
462             exclusive => 0, mode => 0666,
463             size => $key[3]*1024 } } ) {
464 0         0 $self->errstr($@);
465 0         0 $ctie->shunlock;
466 0         0 undef $ctie;
467 0         0 untie %cache;
468 0         0 return undef;
469             }
470 0         0 $xtie->shlock(LOCK_EX);
471 0         0 foreach $id (keys %{$what->{$k}}) {
  0         0  
472 0 0       0 next unless ref $what->{$k}->{$id}; # lex Storable
473             # the cache truncating loop (when pop'ping has to be done for one overstanding
474 0 0       0 if ($self->{Type} == CACHE_LRU) { # element anyway, why not just pop them all?):
475 0         0 while ($key[1] >= $key[0]) {
476 0         0 $idx =~ s/(\d+){1}\/\/\w+?\n$//s;
477 0         0 $rd = "__".$1."__";
478 0         0 chop ($rdpath = $&);
479 0         0 $rdpath =~ s/\/\//\/$k\//;
480 0         0 $key[1]--;
481 0         0 $rdpath = $self->{Base}.$rdpath;
482 0         0 unlink $rdpath;
483             }
484             }
485              
486 0   0     0 $idx ||= "";
487 0 0       0 $idx =~ s/(\d+\/\/$id)\n//s if $idx; # "splice" ...
488 0   0     0 $rdpath = $1 || "";
489              
490             # remove an existing item (items are stored as a whole on one disk and not broken up, and as we
491             # secondly have to stat() an existing item anyway in order to compare the sizes, and after this
492             # comparison maybe ++have++ to delete the old item, we just remove it right here):
493 0 0       0 if ($rdpath) {
494 0         0 $rdpath =~ s/\/\//\/$k\//;
495 0         0 $rdpath = $self->{Base}.$rdpath;
496 0         0 $key[1]--;
497 0         0 unlink $rdpath;
498             }
499 0         0 $item = Storable::freeze($what->{$k}->{$id});
500 0         0 $l = length $item;
501             # find a free rd (__DStart__: new in 0.1.5, see Functions.pm)
502 0         0 for ($rd = $cache{__DStart__}; $rd <= $cache{__Disks__}+$cache{__DStart__}; $rd++) {
503 0         0 $df = df($self->{Base}.$rd);
504 0 0 0     0 last if ($df->{favail} && ($df->{bavail} >= ceil($l/$cache{__BSize__}))); #/
505             }
506 0 0       0 if ($rd > $cache{__Disks__}+$cache{__DStart__}) {
507 0         0 $self->errstr("Cache overflow");
508 0         0 undef $xtie;
509 0         0 untie $idx;
510 0         0 $cache{$k} = join ":", @key;
511 0         0 $ctie->shunlock;
512 0         0 undef $ctie;
513 0         0 untie %cache;
514 0         0 return undef;
515             }
516              
517 0         0 $iline = "$rd//$id\n";
518 0         0 $rdpath = $self->{Base}."$rd/$k/$id";
519 0         0 $hdl = $self->_gensym;
520 0 0       0 sysopen($hdl, $rdpath, O_CREAT | O_RDWR, 0644) || do { $self->errstr("$rdpath: $!");
  0         0  
521 0         0 undef $xtie;
522 0         0 untie $idx;
523 0         0 $cache{$k} = join ":", @key;
524 0         0 $ctie->shunlock;
525 0         0 undef $ctie;
526 0         0 untie %cache;
527 0         0 return undef;
528             };
529 0         0 syswrite $hdl, $item, $l;
530 0         0 close $hdl;
531 0         0 $key[1]++;
532 0         0 $idx = $iline.$idx; # ... and "unshift"
533             }
534 0         0 $cache{$k} = join ":", @key;
535 0         0 $xtie->shunlock;
536 0         0 undef $xtie;
537 0         0 untie $idx;
538             }
539 0         0 $ctie->shunlock;
540 0         0 undef $ctie;
541 0         0 untie %cache;
542 0         0 1;
543             }
544              
545              
546             # also accepts wildcards
547             sub invalidate {
548 1   50 1 1 10 my $self = shift || return undef;
549 1 50 33     5 return undef unless (ref $self || $self->{Err});
550 1         2 $self->{Err} = 0;
551 1         1 my $what = shift;
552 1 50 33     16 unless ($what || ref($what) eq 'HASH') {
553 0         0 $self->errstr("Argument has to be a hashref in call to invalidate");
554 0         0 return undef;
555             }
556              
557 1         16 my (%cache, $ctie);
558 1 50       3 unless (eval { $ctie = tie %cache, 'IPC::Shareable', $self->{ShMem}, { create => 0, destroy => 0,
  1         8  
559             exclusive => 0, mode => 0666,
560             size => 65536 } } ) {
561 1         355 $self->errstr($@);
562 1         3 return undef;
563             }
564 0         0 $ctie->shlock(LOCK_SH);
565              
566 0         0 my ($k, @key, $xtie, $idx, $id, $rd, $rdpath);
567 0         0 foreach $k (keys %{$what}) {
  0         0  
568 0 0       0 next unless exists $cache{$k};
569 0         0 @key = split /:/, $cache{$k}; # [0] = cache value, [1] = items on cache, [2] = ftok, [3] = shmemsize
570              
571 0 0       0 undef $xtie if $xtie;
572 0 0       0 untie $idx if tied $idx;
573 0 0       0 unless (eval { $xtie = tie $idx, 'IPC::Shareable', $key[2], { create => 0, destroy => 0,
  0         0  
574             exclusive => 0, mode => 0666,
575             size => $key[3]*1024 } } ) {
576 0         0 $self->errstr($@);
577 0         0 undef $ctie;
578 0         0 untie %cache;
579 0         0 return undef;
580             }
581 0 0       0 $what->{$k} = [ $what->{$k} ] unless ref $what->{$k} eq 'ARRAY'; # try to normalize input
582 0         0 $xtie->shlock(LOCK_EX);
583 0         0 foreach $id (@{$what->{$k}}) {
  0         0  
584 0         0 $idx =~ s/\d+\/\/$id\n//s; # "splice" ...
585 0 0       0 next unless ($rdpath = $&);
586 0         0 chop $rdpath;
587 0         0 $rdpath =~ s/\/\//\/$k\//;
588 0         0 $rdpath = $self->{Base}.$rdpath;
589 0         0 eval { unlink $rdpath }; # suppress eventual error message if the file doesn't exist.
  0         0  
590 0         0 $key[1]--; # ... and don't unshift :)
591             }
592 0         0 $xtie->shunlock;
593 0         0 undef $xtie;
594 0         0 untie $idx;
595              
596 0         0 $ctie->shlock(LOCK_EX);
597 0         0 $cache{$k} = join ":", @key;
598 0         0 $ctie->shlock(LOCK_UN | LOCK_SH);
599             }
600 0         0 $ctie->shunlock;
601 0         0 undef $ctie;
602 0         0 untie %cache;
603 0         0 1;
604             }
605              
606              
607             # gets the current error message
608             sub errstr {
609 6   50 6 1 29 my $self = shift || return undef;
610 6 50       13 return undef unless ref $self;
611 6 50       14 return 0 unless $self->{Err};
612 0           my @caller = caller;
613 0           $self->{Err}." at $caller[1] line $caller[2]";
614             }
615              
616              
617             ####################################
618             # internal subs
619              
620             # call the appropriate gensym function
621             # args: none,
622             # returns: globref
623             sub _gensym {
624 0   0 0     my $self = shift || return undef;
625 0 0         $ENV{MOD_PERL} ? &Apache::gensym : &Symbol::gensym;
626             }
627              
628              
629             1;