root/feedmelinks/lib/Cache/Memory.pm

Revision 1448, 9.6 kB (checked in by jm3, 2 years ago)

new spammer blacklist, perl libs, and better splinker hunting tools

Line 
1 =head1 NAME
2
3 Cache::Memory - Memory based implementation of the Cache interface
4
5 =head1 SYNOPSIS
6
7   use Cache::Memory;
8
9   my $cache = Cache::Memory->new( namespace => 'MyNamespace',
10                                   default_expires => '600 sec' );
11
12 See Cache for the usage synopsis.
13
14 =head1 DESCRIPTION
15
16 The Cache::Memory class implements the Cache interface.  This cache stores
17 data on a per-process basis.  This is the fastest of the cache
18 implementations, but is memory intensive and data can not be shared between
19 processes.  It also does not persist after the process dies.  However data will
20 remain in the cache until cleared or it expires.  The data will be shared
21 between instances of the cache object, a cache object going out of scope will
22 not destroy the data.
23
24 =cut
25 package Cache::Memory;
26
27 require 5.006;
28 use strict;
29 use warnings;
30 use Heap::Fibonacci;
31 use Cache::Memory::HeapElem;
32 use Cache::Memory::Entry;
33
34 use base qw(Cache);
35 use fields qw(namespace);
36
37 our $VERSION = '2.04';
38
39
40 # storage for all data
41 # data is stored in the form:
42 #   $Store{ns}{key}{data,exp_elem,age_elem,use_elem,rc,validity,handlelock}
43 #
44 # Cache::Memory::Entry elements will be passed the final hash as a reference
45 # when they are constructed.  This reference MUST point to the SAME hash for
46 # all entries (and also must be the hash in Store{ns}{key}) or data
47 # inconsistency will occur.  However this means that the key has to persist in
48 # the store whilst entries exist - regardless of whether there is data stored
49 # in it or not.  In order to allow the Store{ns}{key} to be safely removed, a
50 # 'rc' field is used to track the number of entries that have been created for
51 # the key.
52 my %Store;
53
54 # store sizes
55 my %Store_Sizes;
56
57 # heaps for all the different orderings
58 # Expiry_Heap is shared between all namespaces
59 my Heap $Expiry_Heap = Heap::Fibonacci->new();
60 # In the form $Age_Heaps{namespace} and $Use_Heaps{namespace}
61 my %Age_Heaps;
62 my %Use_Heaps;
63
64
65 my $DEFAULT_NAMESPACE = '_';
66
67
68 =head1 CONSTRUCTOR
69
70   my $cache = Cache::Memory->new( %options )
71
72 The constructor takes cache properties as named arguments, for example:
73
74   my $cache = Cache::Memory->new( namespace => 'MyNamespace',
75                                   default_expires => '600 sec' );
76
77 See 'PROPERTIES' below and in the Cache documentation for a list of all
78 available properties that can be set.
79
80 =cut
81
82 sub new {
83     my Cache::Memory $self = shift;
84     my $args = $#_? { @_ } : shift;
85
86     $self = fields::new($self) unless ref $self;
87     $self->SUPER::new($args);
88
89     my $ns = $args->{namespace} || $DEFAULT_NAMESPACE;
90     $self->{namespace} = $ns;
91
92     # init heaps
93     $Age_Heaps{$ns} ||= Heap::Fibonacci->new();
94     $Use_Heaps{$ns} ||= Heap::Fibonacci->new();
95    
96     return $self;
97 }
98
99 =head1 METHODS
100
101 See 'Cache' for the API documentation.
102
103 =cut
104
105 sub entry {
106     my Cache::Memory $self = shift;
107     my ($key) = @_;
108     my $ns = $self->{namespace};
109
110     $Store{$ns}{$key} ||= {};
111     return Cache::Memory::Entry->new($self, $key, $Store{$ns}{$key});
112 }
113
114 sub purge {
115     #my Cache::Memory $self = shift;
116     my $time = time();
117     while (my $minimum = $Expiry_Heap->minimum) {
118         $minimum->val() <= $time
119             or last;
120         $Expiry_Heap->extract_minimum;
121
122         my $min_key = $minimum->key();
123         my $min_ns = $minimum->namespace();
124
125         my $store_entry = $Store{$min_ns}{$min_key};
126
127         $minimum == delete $store_entry->{exp_elem}
128             or die 'Cache::Memory data structure(s) corrupted';
129
130         # there should always be an age element
131         my $age_elem = delete $store_entry->{age_elem}
132             or die 'Cache::Memory data structure(s) corrupted';
133         $Age_Heaps{$min_ns}->delete($age_elem);
134
135         # there should always be a last use element
136         my $use_elem = delete $store_entry->{use_elem}
137             or die 'Cache::Memory data structure(s) corrupted';
138         $Use_Heaps{$min_ns}->delete($use_elem);
139
140         # remove data & decrease store size
141         $Store_Sizes{$min_ns} -= length(${delete $store_entry->{data}});
142
143         # remove entire entry if there are no active Entry objects
144         delete $Store{$min_ns}{$min_key} unless $store_entry->{rc};
145     }
146 }
147
148 sub clear {
149     my Cache::Memory $self = shift;
150     my $ns = $self->{namespace};
151
152     # empty store & remove elements from expiry heap
153     my $nsstore = $Store{$ns};
154     foreach my $key (keys %$nsstore) {
155         my $store_entry = $nsstore->{$key};
156
157         # simplified form of remove (doesn't deal with heaps)
158         my $exp_elem = delete $store_entry->{exp_elem};
159         $Expiry_Heap->delete($exp_elem) if $exp_elem;
160         delete $store_entry->{age_elem};
161         delete $store_entry->{use_elem};
162         delete $store_entry->{data};
163
164         # remove entire entry if there are no active Entry objects
165         delete $nsstore->{$key} unless $store_entry->{rc};
166     }
167
168     # reset store size
169     $Store_Sizes{$ns} = 0;
170
171     # recreate age and used heaps (thus emptying them)
172     $Age_Heaps{$ns} = Heap::Fibonacci->new();
173     $Use_Heaps{$ns} = Heap::Fibonacci->new();
174 }
175
176 sub count {
177     my Cache::Memory $self = shift;
178     my $count = 0;
179     my $nsstore = $Store{$self->{namespace}};
180     foreach my $key (keys %$nsstore) {
181         $count++ if defined $nsstore->{$key}->{data};
182     }
183     return $count;
184 }
185
186 sub size {
187     my Cache::Memory $self = shift;
188     return $Store_Sizes{$self->{namespace}} || 0;
189 }
190
191
192 =head1 PROPERTIES
193
194 Cache::Memory adds the property 'namespace', which allows you to specify a
195 different caching store area to use from the default.  All methods will work
196 ONLY on the namespace specified.
197
198  my $ns = $c->namespace();
199  $c->set_namespace( $namespace );
200
201 For additional properties, see the 'Cache' documentation.
202
203 =cut
204
205 sub namespace {
206     my Cache::Memory $self = shift;
207     return $self->{namespace};
208 }
209
210 sub set_namespace {
211     my Cache::Memory $self = shift;
212     my ($namespace) = @_;
213     $self->{namespace} = $namespace;
214 }
215
216
217 # REMOVAL STRATEGY METHODS
218
219 sub remove_oldest {
220     my Cache::Memory $self = shift;
221     my $minimum = $Age_Heaps{$self->{namespace}}->minimum
222         or return undef;
223     $minimum == $Store{$minimum->namespace()}{$minimum->key()}{age_elem}
224         or die 'Cache::Memory data structure(s) corrupted';
225     return $self->remove($minimum->key());
226 }
227
228 sub remove_stalest {
229     my Cache::Memory $self = shift;
230     my $minimum = $Use_Heaps{$self->{namespace}}->minimum
231         or return undef;
232     $minimum == $Store{$minimum->namespace()}{$minimum->key()}{use_elem}
233         or die 'Cache::Memory data structure(s) corrupted';
234     return $self->remove($minimum->key());
235 }
236
237
238 # SHORTCUT METHODS
239
240 sub remove {
241     my Cache::Memory $self = shift;
242     my ($key) = @_;
243
244     my $ns = $self->{namespace};
245
246     my $store_entry = $Store{$ns}{$key}
247         or return undef;
248
249     defined $store_entry->{data}
250         or return undef;
251
252     # remove from heap
253     my $exp_elem = delete $store_entry->{exp_elem};
254     $Expiry_Heap->delete($exp_elem) if $exp_elem;
255
256     my $age_elem = delete $store_entry->{age_elem}
257         or die 'Cache::Memory data structure(s) corrupted';
258     $Age_Heaps{$ns}->delete($age_elem);
259
260     my $use_elem = delete $store_entry->{use_elem}
261         or die 'Cache::Memory data structure(s) corrupted';
262     $Use_Heaps{$ns}->delete($use_elem);
263
264     # reduce size of cache iff there is no active handle
265     my $size = 0;
266     my $dataref = delete $store_entry->{data};
267     unless (exists $store_entry->{handlelock}) {
268         $size = length($$dataref);
269         $Store_Sizes{$ns} -= $size;
270     }
271
272     delete $store_entry->{handlelock};
273
274     # remove entire entry if there are no active Entry objects
275     delete $Store{$ns}{$key} unless $store_entry->{rc};
276
277     return $size;
278 }
279
280
281 # UTILITY METHODS
282
283 sub add_expiry_to_heap {
284     my Cache::Memory $self = shift;
285     my ($key, $time) = @_;
286
287     my $exp_elem = Cache::Memory::HeapElem->new($self->{namespace},$key,$time);
288     $Expiry_Heap->add($exp_elem);
289     return $exp_elem;
290 }
291
292 sub del_expiry_from_heap {
293     my Cache::Memory $self = shift;
294     my ($key, $exp_elem) = @_;
295
296     $Expiry_Heap->delete($exp_elem);
297 }
298
299 sub add_age_to_heap {
300     my Cache::Memory $self = shift;
301     my ($key, $time) = @_;
302     my $ns = $self->{namespace};
303
304     my $age_elem = Cache::Memory::HeapElem->new($ns,$key,$time);
305     $Age_Heaps{$ns}->add($age_elem);
306     return $age_elem;
307 }
308
309 sub add_use_to_heap {
310     my Cache::Memory $self = shift;
311     my ($key, $time) = @_;
312     my $ns = $self->{namespace};
313
314     my $use_elem = Cache::Memory::HeapElem->new($ns,$key,$time);
315     $Use_Heaps{$ns}->add($use_elem);
316     return $use_elem;
317 }
318
319 sub update_last_used {
320     my Cache::Memory $self = shift;
321     my ($key) = @_;
322     my $ns = $self->{namespace};
323
324     my $use_elem = $Store{$ns}{$key}{use_elem}
325         or die 'Cache::Memory data structure(s) corrupted';
326
327     $Use_Heaps{$ns}->delete($use_elem);
328     $use_elem->val(time());
329     $Use_Heaps{$ns}->add($use_elem);
330 }
331
332 sub change_size {
333     my Cache::Memory $self = shift;
334     my ($size) = @_;
335     my $ns = $self->{namespace};
336
337     $Store_Sizes{$ns} += $size;
338     $self->check_size($Store_Sizes{$ns}) if $size > 0;
339 }
340
341 sub entry_dropped_final_rc {
342     my Cache::Memory $self = shift;
343     my ($key) = @_;
344     my $ns = $self->{namespace};
345
346     delete $Store{$ns}{$key} unless defined $Store{$ns}{$key}{data};
347 }
348
349
350 1;
351 __END__
352
353 =head1 SEE ALSO
354
355 Cache
356
357 =head1 AUTHOR
358
359  Chris Leishman <chris@leishman.org>
360  Based on work by DeWitt Clinton <dewitt@unto.net>
361
362 =head1 COPYRIGHT
363
364  Copyright (C) 2003-2006 Chris Leishman.  All Rights Reserved.
365
366 This module is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
367 either expressed or implied. This program is free software; you can
368 redistribute or modify it under the same terms as Perl itself.
369
370 $Id: Memory.pm,v 1.9 2006/01/31 15:23:58 caleishm Exp $
371
372 =cut
373
Note: See TracBrowser for help on using the browser.