line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Cache::FastMmap; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Cache::FastMmap - Uses an mmap'ed file to act as a shared memory interprocess cache |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use Cache::FastMmap; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# Uses vaguely sane defaults |
12
|
|
|
|
|
|
|
$Cache = Cache::FastMmap->new(); |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Uses Storable to serialize $Value to bytes for storage |
15
|
|
|
|
|
|
|
$Cache->set($Key, $Value); |
16
|
|
|
|
|
|
|
$Value = $Cache->get($Key); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
$Cache = Cache::FastMmap->new(serializer => ''); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Stores stringified bytes of $Value directly |
21
|
|
|
|
|
|
|
$Cache->set($Key, $Value); |
22
|
|
|
|
|
|
|
$Value = $Cache->get($Key); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 ABSTRACT |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
A shared memory cache through an mmap'ed file. It's core is written |
27
|
|
|
|
|
|
|
in C for performance. It uses fcntl locking to ensure multiple |
28
|
|
|
|
|
|
|
processes can safely access the cache at the same time. It uses |
29
|
|
|
|
|
|
|
a basic LRU algorithm to keep the most used entries in the cache. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 DESCRIPTION |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
In multi-process environments (eg mod_perl, forking daemons, etc), |
34
|
|
|
|
|
|
|
it's common to want to cache information, but have that cache |
35
|
|
|
|
|
|
|
shared between processes. Many solutions already exist, and may |
36
|
|
|
|
|
|
|
suit your situation better: |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=over 4 |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=item * |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
L - acts as a database, data is not automatically |
43
|
|
|
|
|
|
|
expired, slow |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=item * |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
L - hash implementation is broken, data is not automatically |
48
|
|
|
|
|
|
|
expired, slow |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=item * |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
L - lots of features, slow |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=item * |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
L - lots of features, VERY slow. Uses |
57
|
|
|
|
|
|
|
IPC::ShareLite which freeze/thaws ALL data at each read/write |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=item * |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
L - use your favourite RDBMS. can perform well, need a |
62
|
|
|
|
|
|
|
DB server running. very global. socket connection latency |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=item * |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
L - similar to this module, in pure perl. slows down |
67
|
|
|
|
|
|
|
with larger pages |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=item * |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
L - very fast (data ends up mostly in shared memory |
72
|
|
|
|
|
|
|
cache) but acts as a database overall, so data is not automatically |
73
|
|
|
|
|
|
|
expired |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=back |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
In the case I was working on, I needed: |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=over 4 |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item * |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Automatic expiry and space management |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item * |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Very fast access to lots of small items |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item * |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
The ability to fetch/store many items in one go |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=back |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
Which is why I developed this module. It tries to be quite |
96
|
|
|
|
|
|
|
efficient through a number of means: |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=over 4 |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=item * |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Core code is written in C for performance |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=item * |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
It uses multiple pages within a file, and uses Fcntl to only lock |
107
|
|
|
|
|
|
|
a page at a time to reduce contention when multiple processes access |
108
|
|
|
|
|
|
|
the cache. |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=item * |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
It uses a dual level hashing system (hash to find page, then hash |
113
|
|
|
|
|
|
|
within each page to find a slot) to make most C calls O(1) and |
114
|
|
|
|
|
|
|
fast |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=item * |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
On each C, if there are slots and page space available, only |
119
|
|
|
|
|
|
|
the slot has to be updated and the data written at the end of the used |
120
|
|
|
|
|
|
|
data space. If either runs out, a re-organisation of the page is |
121
|
|
|
|
|
|
|
performed to create new slots/space which is done in an efficient way |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=back |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
The class also supports read-through, and write-back or write-through |
126
|
|
|
|
|
|
|
callbacks to access the real data if it's not in the cache, meaning that |
127
|
|
|
|
|
|
|
code like this: |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
my $Value = $Cache->get($Key); |
130
|
|
|
|
|
|
|
if (!defined $Value) { |
131
|
|
|
|
|
|
|
$Value = $RealDataSource->get($Key); |
132
|
|
|
|
|
|
|
$Cache->set($Key, $Value) |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
Isn't required, you instead specify in the constructor: |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
Cache::FastMmap->new( |
138
|
|
|
|
|
|
|
... |
139
|
|
|
|
|
|
|
context => $RealDataSourceHandle, |
140
|
|
|
|
|
|
|
read_cb => sub { $_[0]->get($_[1]) }, |
141
|
|
|
|
|
|
|
write_cb => sub { $_[0]->set($_[1], $_[2]) }, |
142
|
|
|
|
|
|
|
); |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
And then: |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
my $Value = $Cache->get($Key); |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
$Cache->set($Key, $NewValue); |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Will just work and will be read/written to the underlying data source as |
151
|
|
|
|
|
|
|
needed automatically. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head1 PERFORMANCE |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
If you're storing relatively large and complex structures into |
156
|
|
|
|
|
|
|
the cache, then you're limited by the speed of the Storable module. |
157
|
|
|
|
|
|
|
If you're storing simple structures, or raw data, then |
158
|
|
|
|
|
|
|
Cache::FastMmap has noticeable performance improvements. |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
See L for some |
161
|
|
|
|
|
|
|
comparisons to other modules. |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head1 COMPATIBILITY |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Cache::FastMmap uses mmap to map a file as the shared cache space, |
166
|
|
|
|
|
|
|
and fcntl to do page locking. This means it should work on most |
167
|
|
|
|
|
|
|
UNIX like operating systems. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Ash Berlin has written a Win32 layer using MapViewOfFile et al. to |
170
|
|
|
|
|
|
|
provide support for Win32 platform. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head1 MEMORY SIZE |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
Because Cache::FastMmap mmap's a shared file into your processes memory |
175
|
|
|
|
|
|
|
space, this can make each process look quite large, even though it's just |
176
|
|
|
|
|
|
|
mmap'd memory that's shared between all processes that use the cache, |
177
|
|
|
|
|
|
|
and may even be swapped out if the cache is getting low usage. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
However, the OS will think your process is quite large, which might |
180
|
|
|
|
|
|
|
mean you hit some BSD::Resource or 'ulimits' you set previously that you |
181
|
|
|
|
|
|
|
thought were sane, but aren't anymore, so be aware. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=head1 CACHE FILES AND OS ISSUES |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Because Cache::FastMmap uses an mmap'ed file, when you put values into |
186
|
|
|
|
|
|
|
the cache, you are actually "dirtying" pages in memory that belong to |
187
|
|
|
|
|
|
|
the cache file. Your OS will want to write those dirty pages back to |
188
|
|
|
|
|
|
|
the file on the actual physical disk, but the rate it does that at is |
189
|
|
|
|
|
|
|
very OS dependent. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
In Linux, you have some control over how the OS writes those pages |
192
|
|
|
|
|
|
|
back using a number of parameters in /proc/sys/vm |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
dirty_background_ratio |
195
|
|
|
|
|
|
|
dirty_expire_centisecs |
196
|
|
|
|
|
|
|
dirty_ratio |
197
|
|
|
|
|
|
|
dirty_writeback_centisecs |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
How you tune these depends heavily on your setup. |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
As an interesting point, if you use a highmem linux kernel, a change |
202
|
|
|
|
|
|
|
between 2.6.16 and 2.6.20 made the kernel flush memory a LOT more. |
203
|
|
|
|
|
|
|
There's details in this kernel mailing list thread: |
204
|
|
|
|
|
|
|
L |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
In most cases, people are not actually concerned about the persistence |
207
|
|
|
|
|
|
|
of data in the cache, and so are happy to disable writing of any cache |
208
|
|
|
|
|
|
|
data back to disk at all. Baically what they want is an in memory only |
209
|
|
|
|
|
|
|
shared cache. The best way to do that is to use a "tmpfs" filesystem |
210
|
|
|
|
|
|
|
and put all cache files on there. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
For instance, all our machines have a /tmpfs mount point that we |
213
|
|
|
|
|
|
|
create in /etc/fstab as: |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
none /tmpfs tmpfs defaults,noatime,size=1000M 0 0 |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
And we put all our cache files on there. The tmpfs filesystem is smart |
218
|
|
|
|
|
|
|
enough to only use memory as required by files actually on the tmpfs, |
219
|
|
|
|
|
|
|
so making it 1G in size doesn't actually use 1G of memory, it only uses |
220
|
|
|
|
|
|
|
as much as the cache files we put on it. In all cases, we ensure that |
221
|
|
|
|
|
|
|
we never run out of real memory, so the cache files effectively act |
222
|
|
|
|
|
|
|
just as named access points to shared memory. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Some people have suggested using anonymous mmaped memory. Unfortunately |
225
|
|
|
|
|
|
|
we need a file descriptor to do the fcntl locking on, so we'd have |
226
|
|
|
|
|
|
|
to create a separate file on a filesystem somewhere anyway. It seems |
227
|
|
|
|
|
|
|
easier to just create an explicit "tmpfs" filesystem. |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=head1 PAGE SIZE AND KEY/VALUE LIMITS |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
To reduce lock contention, Cache::FastMmap breaks up the file |
232
|
|
|
|
|
|
|
into pages. When you get/set a value, it hashes the key to get a page, |
233
|
|
|
|
|
|
|
then locks that page, and uses a hash table within the page to |
234
|
|
|
|
|
|
|
get/store the actual key/value pair. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
One consequence of this is that you cannot store values larger than |
237
|
|
|
|
|
|
|
a page in the cache at all. Attempting to store values larger than |
238
|
|
|
|
|
|
|
a page size will fail (the set() function will return false). |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Also keep in mind that each page has it's own hash table, and that we |
241
|
|
|
|
|
|
|
store the key and value data of each item. So if you are expecting to |
242
|
|
|
|
|
|
|
store large values and/or keys in the cache, you should use page sizes |
243
|
|
|
|
|
|
|
that are definitely larger than your largest key + value size + a few |
244
|
|
|
|
|
|
|
kbytes for the overhead. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head1 USAGE |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Because the cache uses shared memory through an mmap'd file, you have |
249
|
|
|
|
|
|
|
to make sure each process connects up to the file. There's probably |
250
|
|
|
|
|
|
|
two main ways to do this: |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=over 4 |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=item * |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Create the cache in the parent process, and then when it forks, each |
257
|
|
|
|
|
|
|
child will inherit the same file descriptor, mmap'ed memory, etc and |
258
|
|
|
|
|
|
|
just work. This is the recommended way. (BEWARE: This only works under |
259
|
|
|
|
|
|
|
UNIX as Win32 has no concept of forking) |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=item * |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
Explicitly connect up in each forked child to the share file. In this |
264
|
|
|
|
|
|
|
case, make sure the file already exists and the children connect with |
265
|
|
|
|
|
|
|
init_file => 0 to avoid deleting the cache contents and possible |
266
|
|
|
|
|
|
|
race corruption conditions. Also be careful that multiple children |
267
|
|
|
|
|
|
|
may race to create the file at the same time, each overwriting and |
268
|
|
|
|
|
|
|
corrupting content. Use a separate lock file if you must to ensure |
269
|
|
|
|
|
|
|
only one child creates the file. (This is the only possible way under |
270
|
|
|
|
|
|
|
Win32) |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=back |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
The first way is usually the easiest. If you're using the cache in a |
275
|
|
|
|
|
|
|
Net::Server based module, you'll want to open the cache in the |
276
|
|
|
|
|
|
|
C, because that's executed before the fork, but after |
277
|
|
|
|
|
|
|
the process ownership has changed and any chroot has been done. |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
In mod_perl, just open the cache at the global level in the appropriate |
280
|
|
|
|
|
|
|
module, which is executed as the server is starting and before it |
281
|
|
|
|
|
|
|
starts forking children, but you'll probably want to chmod or chown |
282
|
|
|
|
|
|
|
the file to the permissions of the apache process. |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=head1 METHODS |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=over 4 |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=cut |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# Modules/Export/XSLoader {{{ |
291
|
17
|
|
|
17
|
|
336440
|
use 5.006; |
|
17
|
|
|
|
|
48
|
|
292
|
17
|
|
|
17
|
|
73
|
use strict; |
|
17
|
|
|
|
|
24
|
|
|
17
|
|
|
|
|
382
|
|
293
|
17
|
|
|
17
|
|
71
|
use warnings; |
|
17
|
|
|
|
|
28
|
|
|
17
|
|
|
|
|
582
|
|
294
|
17
|
|
|
17
|
|
9260
|
use bytes; |
|
17
|
|
|
|
|
145
|
|
|
17
|
|
|
|
|
71
|
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
our $VERSION = '1.45'; |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
require XSLoader; |
299
|
|
|
|
|
|
|
XSLoader::load('Cache::FastMmap', $VERSION); |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# Track currently live caches so we can cleanup in END {} |
302
|
|
|
|
|
|
|
# if we have empty_on_exit set |
303
|
|
|
|
|
|
|
our %LiveCaches; |
304
|
|
|
|
|
|
|
|
305
|
17
|
|
|
17
|
|
1122
|
use constant FC_ISDIRTY => 1; |
|
17
|
|
|
|
|
22
|
|
|
17
|
|
|
|
|
62897
|
|
306
|
|
|
|
|
|
|
# }}} |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=item I |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
Create a new Cache::FastMmap object. |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
Basic global parameters are: |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=over 4 |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=item * B |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
File to mmap for sharing of data. |
319
|
|
|
|
|
|
|
default on unix: /tmp/sharefile-$pid-$time-$random |
320
|
|
|
|
|
|
|
default on windows: %TEMP%\sharefile-$pid-$time-$random |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=item * B |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
Clear any existing values and re-initialise file. Useful to do in a |
325
|
|
|
|
|
|
|
parent that forks off children to ensure that file is empty at the start |
326
|
|
|
|
|
|
|
(default: 0) |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
B This is quite important to do in the parent to ensure a |
329
|
|
|
|
|
|
|
consistent file structure. The shared file is not perfectly transaction |
330
|
|
|
|
|
|
|
safe, and so if a child is killed at the wrong instant, it might leave |
331
|
|
|
|
|
|
|
the cache file in an inconsistent state. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=item * B |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Use a serialization library to serialize perl data structures before |
336
|
|
|
|
|
|
|
storing in the cache. If not set, the raw value in the variable passed |
337
|
|
|
|
|
|
|
to set() is stored as a string. You must set this if you want to store |
338
|
|
|
|
|
|
|
anything other than basic scalar values. Supported values are: |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
'' for none |
341
|
|
|
|
|
|
|
'storable' for 'Storable' |
342
|
|
|
|
|
|
|
'sereal' for 'Sereal' |
343
|
|
|
|
|
|
|
'json' for 'JSON' |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
If this parameter has a value the module will attempt to load the |
346
|
|
|
|
|
|
|
associated package and then use the API of that package to serialize data |
347
|
|
|
|
|
|
|
before storing in the cache, and deserialize it upon retrieval from the |
348
|
|
|
|
|
|
|
cache. (default: 'storable') |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
(Note: Historically this module only supported a boolean value for the |
351
|
|
|
|
|
|
|
`raw_values` parameter and defaulted to 0, which meant it used Storable |
352
|
|
|
|
|
|
|
to serialze all values.) |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=item * B |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
Deprecated. Use B above |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=item * B |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
Compress the value (but not the key) before storing into the cache, using |
361
|
|
|
|
|
|
|
the compression package identified by the value of the parameter. Supported |
362
|
|
|
|
|
|
|
values are: |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
'zlib' for 'Compress::Zlib' |
365
|
|
|
|
|
|
|
'lz4' for 'Compress::LZ4' |
366
|
|
|
|
|
|
|
'snappy' for 'Compress::Snappy' |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
If this parameter has a value the module will attempt to load the |
369
|
|
|
|
|
|
|
associated package and then use the API of that package to compress data |
370
|
|
|
|
|
|
|
before storing in the cache, and uncompress it upon retrieval from the |
371
|
|
|
|
|
|
|
cache. (default: undef) |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
(Note: Historically this module only supported a boolean value for the |
374
|
|
|
|
|
|
|
`compress` parameter and defaulted to use Compress::Zlib. The note for the |
375
|
|
|
|
|
|
|
old `compress` parameter stated: "Some initial testing shows that the |
376
|
|
|
|
|
|
|
uncompressing tends to be very fast, though the compressing can be quite |
377
|
|
|
|
|
|
|
slow, so it's probably best to use this option only if you know values in |
378
|
|
|
|
|
|
|
the cache are long-lived and have a high hit rate." |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
Comparable test results for the other compression tools are not yet available; |
381
|
|
|
|
|
|
|
submission of benchmarks welcome. However, the documentation for the 'Snappy' |
382
|
|
|
|
|
|
|
library (http://google.github.io/snappy/) states: For instance, compared to |
383
|
|
|
|
|
|
|
the fastest mode of zlib, Snappy is an order of magnitude faster for most |
384
|
|
|
|
|
|
|
inputs, but the resulting compressed files are anywhere from 20% to 100% |
385
|
|
|
|
|
|
|
bigger. ) |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
=item * B |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
Deprecated. Please use B, see above. |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=item * B |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
Enable some basic statistics capturing. When enabled, every read to |
394
|
|
|
|
|
|
|
the cache is counted, and every read to the cache that finds a value |
395
|
|
|
|
|
|
|
in the cache is also counted. You can then retrieve these values |
396
|
|
|
|
|
|
|
via the get_statistics() call. This causes every read action to |
397
|
|
|
|
|
|
|
do a write on a page, which can cause some more IO, so it's |
398
|
|
|
|
|
|
|
disabled by default. (default: 0) |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=item * B |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
Maximum time to hold values in the cache in seconds. A value of 0 |
403
|
|
|
|
|
|
|
means does no explicit expiry time, and values are expired only based |
404
|
|
|
|
|
|
|
on LRU usage. Can be expressed as 1m, 1h, 1d for minutes/hours/days |
405
|
|
|
|
|
|
|
respectively. (default: 0) |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=back |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
You may specify the cache size as: |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
=over 4 |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=item * B |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
Size of cache. Can be expresses as 1k, 1m for kilobytes or megabytes |
416
|
|
|
|
|
|
|
respectively. Automatically guesses page size/page count values. |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=back |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
Or specify explicit page size/page count values. If none of these are |
421
|
|
|
|
|
|
|
specified, the values page_size = 64k and num_pages = 89 are used. |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=over 4 |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=item * B |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
Size of each page. Must be a power of 2 between 4k and 1024k. If not, |
428
|
|
|
|
|
|
|
is rounded to the nearest value. |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=item * B |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
Number of pages. Should be a prime number for best hashing |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=back |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
The cache allows the use of callbacks for reading/writing data to an |
437
|
|
|
|
|
|
|
underlying data store. |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=over 4 |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=item * B |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
Opaque reference passed as the first parameter to any callback function |
444
|
|
|
|
|
|
|
if specified |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=item * B |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
Callback to read data from the underlying data store. Called as: |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
$read_cb->($context, $Key) |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Should return the value to use. This value will be saved in the cache |
453
|
|
|
|
|
|
|
for future retrievals. Return undef if there is no value for the |
454
|
|
|
|
|
|
|
given key |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=item * B |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
Callback to write data to the underlying data store. |
459
|
|
|
|
|
|
|
Called as: |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
$write_cb->($context, $Key, $Value, $ExpiryTime) |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
In 'write_through' mode, it's always called as soon as a I |
464
|
|
|
|
|
|
|
is called on the Cache::FastMmap class. In 'write_back' mode, it's |
465
|
|
|
|
|
|
|
called when a value is expunged from the cache if it's been changed |
466
|
|
|
|
|
|
|
by a I rather than read from the underlying store with the |
467
|
|
|
|
|
|
|
I above. |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Note: Expired items do result in the I being |
470
|
|
|
|
|
|
|
called if 'write_back' caching is enabled and the item has been |
471
|
|
|
|
|
|
|
changed. You can check the $ExpiryTime against C |
472
|
|
|
|
|
|
|
want to write back values which aren't expired. |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Also remember that I may be called in a different process |
475
|
|
|
|
|
|
|
to the one that placed the data in the cache in the first place |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=item * B |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Callback to delete data from the underlying data store. Called as: |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
$delete_cb->($context, $Key) |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
Called as soon as I is called on the Cache::FastMmap class |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
=item * B |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
If set to true, then if the I is called and it returns |
488
|
|
|
|
|
|
|
undef to say nothing was found, then that information is stored |
489
|
|
|
|
|
|
|
in the cache, so that next time a I is called on that |
490
|
|
|
|
|
|
|
key, undef is returned immediately rather than again calling |
491
|
|
|
|
|
|
|
the I |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=item * B |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
Either 'write_back' or 'write_through'. (default: write_through) |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=item * B |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
If you're using a callback function, then normally the cache is not |
500
|
|
|
|
|
|
|
re-enterable, and attempting to call a get/set on the cache will |
501
|
|
|
|
|
|
|
cause an error. By setting this to one, the cache will unlock any |
502
|
|
|
|
|
|
|
pages before calling the callback. During the unlock time, other |
503
|
|
|
|
|
|
|
processes may change data in current cache page, causing possible |
504
|
|
|
|
|
|
|
unexpected effects. You shouldn't set this unless you know you |
505
|
|
|
|
|
|
|
want to be able to recall to the cache within a callback. |
506
|
|
|
|
|
|
|
(default: 0) |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=item * B |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
When you have 'write_back' mode enabled, then |
511
|
|
|
|
|
|
|
you really want to make sure all values from the cache are expunged |
512
|
|
|
|
|
|
|
when your program exits so any changes are written back. |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
The trick is that we only want to do this in the parent process, |
515
|
|
|
|
|
|
|
we don't want any child processes to empty the cache when they exit. |
516
|
|
|
|
|
|
|
So if you set this, it takes the PID via $$, and only calls |
517
|
|
|
|
|
|
|
empty in the DESTROY method if $$ matches the pid we captured |
518
|
|
|
|
|
|
|
at the start. (default: 0) |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=item * B |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
Unlink the share file when the cache is destroyed. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
As with empty_on_exit, this will only unlink the file if the |
525
|
|
|
|
|
|
|
DESTROY occurs in the same PID that the cache was created in |
526
|
|
|
|
|
|
|
so that any forked children don't unlink the file. |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
This value defaults to 1 if the share_file specified does |
529
|
|
|
|
|
|
|
not already exist. If the share_file specified does already |
530
|
|
|
|
|
|
|
exist, it defaults to 0. |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=item * B |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
Sets an alarm(10) before each page is locked via fcntl(F_SETLKW) to catch |
535
|
|
|
|
|
|
|
any deadlock. This used to be the default behaviour, but it's not really |
536
|
|
|
|
|
|
|
needed in the default case and could clobber sub-second Time::HiRes |
537
|
|
|
|
|
|
|
alarms setup by other code. Defaults to 0. |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=back |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
=cut |
542
|
|
|
|
|
|
|
sub new { |
543
|
25
|
|
|
25
|
1
|
60707
|
my $Proto = shift; |
544
|
25
|
|
33
|
|
|
159
|
my $Class = ref($Proto) || $Proto; |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
# If first item is a hash ref, use it as arguments |
547
|
25
|
50
|
|
|
|
224
|
my %Args = ref($_[0]) eq 'HASH' ? %{shift()} : @_; |
|
0
|
|
|
|
|
0
|
|
548
|
|
|
|
|
|
|
|
549
|
25
|
|
|
|
|
56
|
my $Self = {}; |
550
|
25
|
|
|
|
|
49
|
bless ($Self, $Class); |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
# Work out cache file and whether to init |
553
|
25
|
|
|
|
|
58
|
my $share_file = $Args{share_file}; |
554
|
25
|
50
|
|
|
|
79
|
if (!$share_file) { |
555
|
25
|
|
50
|
|
|
240
|
my $tmp_dir = $ENV{TMPDIR} || "/tmp"; |
556
|
25
|
|
50
|
|
|
144
|
my $win_tmp_dir = $ENV{TEMP} || "c:\\"; |
557
|
25
|
50
|
|
|
|
141
|
$share_file = ($^O eq "MSWin32" ? "$win_tmp_dir\\sharefile" : "$tmp_dir/sharefile"); |
558
|
25
|
|
|
|
|
775
|
$share_file .= "-" . $$ . "-" . time . "-" . int(rand(100000)); |
559
|
|
|
|
|
|
|
} |
560
|
25
|
50
|
|
|
|
87
|
!ref($share_file) || die "share_file argument was a reference"; |
561
|
25
|
|
|
|
|
168
|
$Self->{share_file} = $share_file; |
562
|
|
|
|
|
|
|
|
563
|
25
|
100
|
|
|
|
88
|
my $init_file = $Args{init_file} ? 1 : 0; |
564
|
25
|
50
|
|
|
|
69
|
my $test_file = $Args{test_file} ? 1 : 0; |
565
|
25
|
100
|
|
|
|
61
|
my $enable_stats = $Args{enable_stats} ? 1 : 0; |
566
|
25
|
50
|
|
|
|
72
|
my $catch_deadlocks = $Args{catch_deadlocks} ? 1 : 0; |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
# Worth out unlink default if not specified |
569
|
25
|
50
|
|
|
|
76
|
if (!exists $Args{unlink_on_exit}) { |
570
|
25
|
50
|
|
|
|
1752
|
$Args{unlink_on_exit} = -f($share_file) ? 0 : 1; |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
# Serialise stored values? |
574
|
25
|
50
|
66
|
|
|
111
|
my $serializer = $Args{serializer} // ($Args{raw_values} ? '' : 'storable'); |
575
|
|
|
|
|
|
|
|
576
|
25
|
100
|
|
|
|
75
|
if ($serializer) { |
577
|
5
|
50
|
|
|
|
17
|
if ($serializer eq 'storable') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
578
|
5
|
50
|
|
|
|
364
|
eval "require Storable;" |
579
|
|
|
|
|
|
|
|| die "Could not load serialization package: Storable : $@"; |
580
|
5
|
|
|
|
|
10824
|
$Self->{serialize} = Storable->can("freeze"); |
581
|
5
|
|
|
|
|
24
|
$Self->{deserialize} = Storable->can("thaw"); |
582
|
|
|
|
|
|
|
} elsif ($serializer eq 'sereal') { |
583
|
0
|
0
|
|
|
|
0
|
eval "require Sereal::Encoder; require Sereal::Decoder;" |
584
|
|
|
|
|
|
|
|| die "Could not load serialization package: Sereal : $@"; |
585
|
0
|
|
|
|
|
0
|
my $SerealEnc = Sereal::Encoder->new(); |
586
|
0
|
|
|
|
|
0
|
my $SerealDec = Sereal::Decoder->new(); |
587
|
0
|
|
|
0
|
|
0
|
$Self->{serialize} = sub { $SerealEnc->encode(@_); }; |
|
0
|
|
|
|
|
0
|
|
588
|
0
|
|
|
0
|
|
0
|
$Self->{deserialize} = sub { $SerealDec->decode(@_); }; |
|
0
|
|
|
|
|
0
|
|
589
|
|
|
|
|
|
|
} elsif ($serializer eq 'json') { |
590
|
0
|
0
|
|
|
|
0
|
eval "require JSON;" |
591
|
|
|
|
|
|
|
|| die "Could not load serialization package: JSON : $@"; |
592
|
0
|
|
|
|
|
0
|
my $JSON = JSON->new->utf8->allow_nonref; |
593
|
0
|
|
|
0
|
|
0
|
$Self->{serialize} = sub { $JSON->encode(${$_[0]}); }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
594
|
0
|
|
|
0
|
|
0
|
$Self->{deserialize} = sub { \$JSON->decode($_[0]); }; |
|
0
|
|
|
|
|
0
|
|
595
|
|
|
|
|
|
|
} else { |
596
|
0
|
|
|
|
|
0
|
die "Unrecognized value >$serializer< for `serializer` parameter"; |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
# Compress stored values? |
601
|
25
|
100
|
66
|
|
|
152
|
my $compressor = $Args{compressor} // ($Args{compress} ? 'zlib' : 0); |
602
|
|
|
|
|
|
|
|
603
|
25
|
|
|
|
|
132
|
my %known_compressors = ( |
604
|
|
|
|
|
|
|
zlib => 'Compress::Zlib', |
605
|
|
|
|
|
|
|
lz4 => 'Compress::LZ4', |
606
|
|
|
|
|
|
|
snappy => 'Compress::Snappy', |
607
|
|
|
|
|
|
|
); |
608
|
|
|
|
|
|
|
|
609
|
25
|
100
|
|
|
|
84
|
if ( $compressor ) { |
610
|
4
|
|
50
|
|
|
12
|
my $compressor_module = $known_compressors{$compressor} |
611
|
|
|
|
|
|
|
|| die "Unrecognized value >$compressor< for `compressor` parameter"; |
612
|
|
|
|
|
|
|
|
613
|
4
|
50
|
|
|
|
279
|
if ( ! eval "require $compressor_module;" ) { |
614
|
0
|
|
|
|
|
0
|
die "Could not load compression package: $compressor_module : $@"; |
615
|
|
|
|
|
|
|
} else { |
616
|
|
|
|
|
|
|
# LZ4 and Snappy use same API |
617
|
4
|
50
|
33
|
|
|
30
|
if ($compressor_module eq 'Compress::LZ4' || $compressor_module eq 'Compress::Snappy') { |
|
|
50
|
|
|
|
|
|
618
|
0
|
|
|
|
|
0
|
$Self->{compress} = $compressor_module->can("compress"); |
619
|
0
|
|
|
|
|
0
|
$Self->{uncompress} = $compressor_module->can("uncompress"); |
620
|
|
|
|
|
|
|
} elsif ($compressor_module eq 'Compress::Zlib') { |
621
|
4
|
|
|
|
|
30
|
$Self->{compress} = $compressor_module->can("memGzip"); |
622
|
|
|
|
|
|
|
# (gunzip from tmp var: https://rt.cpan.org/Ticket/Display.html?id=72945) |
623
|
4
|
|
|
|
|
14
|
my $uncompress = $compressor_module->can("memGunzip"); |
624
|
4
|
|
|
2
|
|
24
|
$Self->{uncompress} = sub { &$uncompress(my $Tmp = shift) }; |
|
2
|
|
|
|
|
7
|
|
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
# If using empty_on_exit, need to track used caches |
630
|
25
|
|
100
|
|
|
205
|
my $empty_on_exit = $Self->{empty_on_exit} = int($Args{empty_on_exit} || 0); |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
# Need Scalar::Util::weaken to track open caches |
633
|
25
|
100
|
|
|
|
87
|
if ($empty_on_exit) { |
634
|
1
|
50
|
|
1
|
|
72
|
eval "use Scalar::Util qw(weaken); 1;" |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
64
|
|
635
|
|
|
|
|
|
|
|| die "Could not load Scalar::Util module: $@"; |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# Work out expiry time in seconds |
639
|
25
|
|
|
|
|
120
|
my $expire_time = $Self->{expire_time} = parse_expire_time($Args{expire_time}); |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# Function rounds to the nearest power of 2 |
642
|
25
|
|
|
25
|
0
|
180
|
sub RoundPow2 { return int(2 ** int(log($_[0])/log(2)) + 0.1); } |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
# Work out cache size |
645
|
25
|
|
|
|
|
51
|
my ($cache_size, $num_pages, $page_size); |
646
|
|
|
|
|
|
|
|
647
|
25
|
|
|
|
|
71
|
my %Sizes = (k => 1024, m => 1024*1024); |
648
|
25
|
50
|
|
|
|
226
|
if ($cache_size = $Args{cache_size}) { |
649
|
0
|
0
|
|
|
|
0
|
$cache_size *= $Sizes{lc($1)} if $cache_size =~ s/([km])$//i; |
650
|
|
|
|
|
|
|
|
651
|
0
|
0
|
|
|
|
0
|
if ($num_pages = $Args{num_pages}) { |
652
|
0
|
|
|
|
|
0
|
$page_size = RoundPow2($cache_size / $num_pages); |
653
|
0
|
0
|
|
|
|
0
|
$page_size = 4096 if $page_size < 4096; |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
} else { |
656
|
0
|
|
0
|
|
|
0
|
$page_size = $Args{page_size} || 65536; |
657
|
0
|
0
|
|
|
|
0
|
$page_size *= $Sizes{lc($1)} if $page_size =~ s/([km])$//i; |
658
|
0
|
0
|
|
|
|
0
|
$page_size = 4096 if $page_size < 4096; |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
# Increase num_pages till we exceed |
661
|
0
|
|
|
|
|
0
|
$num_pages = 89; |
662
|
0
|
0
|
|
|
|
0
|
if ($num_pages * $page_size <= $cache_size) { |
663
|
0
|
|
|
|
|
0
|
while ($num_pages * $page_size <= $cache_size) { |
664
|
0
|
|
|
|
|
0
|
$num_pages = $num_pages * 2 + 1; |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
} else { |
667
|
0
|
|
|
|
|
0
|
while ($num_pages * $page_size > $cache_size) { |
668
|
0
|
|
|
|
|
0
|
$num_pages = int(($num_pages-1) / 2); |
669
|
|
|
|
|
|
|
} |
670
|
0
|
|
|
|
|
0
|
$num_pages = $num_pages * 2 + 1; |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
} |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
} else { |
676
|
25
|
|
|
|
|
74
|
($num_pages, $page_size) = @Args{qw(num_pages page_size)}; |
677
|
25
|
|
100
|
|
|
85
|
$num_pages ||= 89; |
678
|
25
|
|
100
|
|
|
70
|
$page_size ||= 65536; |
679
|
25
|
50
|
|
|
|
150
|
$page_size *= $Sizes{lc($1)} if $page_size =~ s/([km])$//i; |
680
|
25
|
|
|
|
|
75
|
$page_size = RoundPow2($page_size); |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
|
683
|
25
|
|
|
|
|
42
|
$cache_size = $num_pages * $page_size; |
684
|
25
|
|
|
|
|
99
|
@$Self{qw(cache_size num_pages page_size)} |
685
|
|
|
|
|
|
|
= ($cache_size, $num_pages, $page_size); |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
# Number of slots to start in each page |
688
|
25
|
|
50
|
|
|
220
|
my $start_slots = int($Args{start_slots} || 0) || 89; |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
# Save read through/write back/write through details |
691
|
25
|
|
100
|
|
|
111
|
my $write_back = ($Args{write_action} || 'write_through') eq 'write_back'; |
692
|
|
|
|
|
|
|
@$Self{qw(context read_cb write_cb delete_cb)} |
693
|
25
|
|
|
|
|
145
|
= @Args{qw(context read_cb write_cb delete_cb)}; |
694
|
|
|
|
|
|
|
@$Self{qw(cache_not_found allow_recursive write_back)} |
695
|
25
|
|
|
|
|
108
|
= (@Args{qw(cache_not_found allow_recursive)}, $write_back); |
696
|
|
|
|
|
|
|
@$Self{qw(unlink_on_exit enable_stats)} |
697
|
25
|
|
|
|
|
76
|
= (@Args{qw(unlink_on_exit)}, $enable_stats); |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
# Save pid |
700
|
25
|
|
|
|
|
73
|
$Self->{pid} = $$; |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
# Initialise C cache code |
703
|
25
|
|
|
|
|
135
|
my $Cache = fc_new(); |
704
|
|
|
|
|
|
|
|
705
|
25
|
|
|
|
|
49
|
$Self->{Cache} = $Cache; |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
# Setup cache parameters |
708
|
25
|
|
|
|
|
292
|
fc_set_param($Cache, 'init_file', $init_file); |
709
|
25
|
|
|
|
|
82
|
fc_set_param($Cache, 'init_file', $init_file); |
710
|
25
|
|
|
|
|
62
|
fc_set_param($Cache, 'test_file', $test_file); |
711
|
25
|
|
|
|
|
70
|
fc_set_param($Cache, 'page_size', $page_size); |
712
|
25
|
|
|
|
|
51
|
fc_set_param($Cache, 'num_pages', $num_pages); |
713
|
25
|
|
|
|
|
56
|
fc_set_param($Cache, 'expire_time', $expire_time); |
714
|
25
|
|
|
|
|
43
|
fc_set_param($Cache, 'share_file', $share_file); |
715
|
25
|
|
|
|
|
77
|
fc_set_param($Cache, 'start_slots', $start_slots); |
716
|
25
|
|
|
|
|
52
|
fc_set_param($Cache, 'catch_deadlocks', $catch_deadlocks); |
717
|
25
|
|
|
|
|
91
|
fc_set_param($Cache, 'enable_stats', $enable_stats); |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
# And initialise it |
720
|
25
|
|
|
|
|
143331
|
fc_init($Cache); |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
# Track cache if need to empty on exit |
723
|
25
|
100
|
|
|
|
168
|
weaken($LiveCaches{ref($Self)} = $Self) |
724
|
|
|
|
|
|
|
if $empty_on_exit; |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
# All done, return PERL hash ref as class |
727
|
25
|
|
|
|
|
318
|
return $Self; |
728
|
|
|
|
|
|
|
} |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=item I |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
Search cache for given Key. Returns undef if not found. If |
733
|
|
|
|
|
|
|
I specified and not found, calls the callback to try |
734
|
|
|
|
|
|
|
and find the value for the key, and if found (or 'cache_not_found' |
735
|
|
|
|
|
|
|
is set), stores it into the cache and returns the found value. |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
I<%Options> is optional, and is used by get_and_set() to control |
738
|
|
|
|
|
|
|
the locking behaviour. For now, you should probably ignore it |
739
|
|
|
|
|
|
|
unless you read the code to understand how it works |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
=cut |
742
|
|
|
|
|
|
|
sub get { |
743
|
165775
|
|
|
165775
|
1
|
1785743
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
# Hash value, lock page, read result |
746
|
165775
|
|
|
|
|
255781
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
747
|
165775
|
|
|
|
|
195466
|
my $Unlock = $Self->_lock_page($HashPage); |
748
|
165774
|
|
|
|
|
428504
|
my ($Val, $Flags, $Found) = fc_read($Cache, $HashSlot, $_[1]); |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
# Value not found, check underlying data store |
751
|
165774
|
100
|
100
|
|
|
336850
|
if (!$Found && (my $read_cb = $Self->{read_cb})) { |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
# Callback to read from underlying data store |
754
|
|
|
|
|
|
|
# (unlock page first if we allow recursive calls |
755
|
13116
|
100
|
|
|
|
16552
|
$Unlock = undef if $Self->{allow_recursive}; |
756
|
13116
|
|
|
|
|
10446
|
$Val = eval { $read_cb->($Self->{context}, $_[1]); }; |
|
13116
|
|
|
|
|
17352
|
|
757
|
13116
|
|
|
|
|
35633
|
my $Err = $@; |
758
|
13116
|
100
|
|
|
|
16524
|
$Unlock = $Self->_lock_page($HashPage) if $Self->{allow_recursive}; |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
# Pass on any error |
761
|
13116
|
100
|
|
|
|
15808
|
if ($Err) { |
762
|
1
|
|
|
|
|
3
|
die $Err; |
763
|
|
|
|
|
|
|
} |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
# If we found it, or want to cache not-found, store back into our cache |
766
|
13115
|
50
|
66
|
|
|
20177
|
if (defined $Val || $Self->{cache_not_found}) { |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
# Are we doing writeback's? If so, need to mark as dirty in cache |
769
|
13115
|
|
|
|
|
10180
|
my $write_back = $Self->{write_back}; |
770
|
|
|
|
|
|
|
|
771
|
13115
|
100
|
|
|
|
15481
|
$Val = $Self->{serialize}(\$Val) if $Self->{serialize}; |
772
|
13115
|
50
|
|
|
|
15599
|
$Val = $Self->{compress}($Val) if $Self->{compress}; |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
# Get key/value len (we've got 'use bytes'), and do expunge check to |
775
|
|
|
|
|
|
|
# create space if needed |
776
|
13115
|
100
|
|
|
|
17108
|
my $KVLen = length($_[1]) + (defined($Val) ? length($Val) : 0); |
777
|
13115
|
|
|
|
|
15374
|
$Self->_expunge_page(2, 1, $KVLen); |
778
|
|
|
|
|
|
|
|
779
|
13115
|
|
|
|
|
27139
|
fc_write($Cache, $HashSlot, $_[1], $Val, -1, 0); |
780
|
|
|
|
|
|
|
} |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
# Unlock page and return any found value |
784
|
|
|
|
|
|
|
# Unlock is done only if we're not in the middle of a get_set() operation. |
785
|
165773
|
|
66
|
|
|
240052
|
my $SkipUnlock = $_[2] && $_[2]->{skip_unlock}; |
786
|
165773
|
100
|
|
|
|
216233
|
$Unlock = undef unless $SkipUnlock; |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
# If not using raw values, use thaw() to turn data back into object |
789
|
165773
|
100
|
66
|
|
|
236051
|
$Val = $Self->{uncompress}($Val) if defined($Val) && $Self->{compress}; |
790
|
165773
|
100
|
66
|
|
|
312191
|
$Val = ${$Self->{deserialize}($Val)} if defined($Val) && $Self->{deserialize}; |
|
99309
|
|
|
|
|
146492
|
|
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
# If explicitly asked to skip unlocking, we return the reference to the unlocker |
793
|
165773
|
100
|
|
|
|
902177
|
return ($Val, $Unlock) if $SkipUnlock; |
794
|
|
|
|
|
|
|
|
795
|
145767
|
|
|
|
|
201776
|
return $Val; |
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
=item I |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
Store specified key/value pair into cache |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
I<%Options> is optional, and is used by get_and_set() to control |
803
|
|
|
|
|
|
|
the locking behaviour. For now, you should probably ignore it |
804
|
|
|
|
|
|
|
unless you read the code to understand how it works |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
This method returns true if the value was stored in the cache, |
807
|
|
|
|
|
|
|
false otherwise. See the PAGE SIZE AND KEY/VALUE LIMITS section |
808
|
|
|
|
|
|
|
for more details. |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=cut |
811
|
|
|
|
|
|
|
sub set { |
812
|
40885
|
|
|
40885
|
1
|
11896289
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
813
|
|
|
|
|
|
|
|
814
|
40885
|
100
|
|
|
|
70269
|
my $Val = $Self->{serialize} ? $Self->{serialize}(\$_[2]) : $_[2]; |
815
|
40885
|
100
|
|
|
|
134203
|
$Val = $Self->{compress}($Val) if $Self->{compress}; |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
# Get opts, make compatible with Cache::Cache interface |
818
|
40885
|
100
|
|
|
|
66863
|
my $Opts = defined($_[3]) ? (ref($_[3]) ? $_[3] : { expire_time => $_[3] }) : undef; |
|
|
100
|
|
|
|
|
|
819
|
40885
|
100
|
66
|
|
|
81862
|
my $expire_seconds = defined($Opts && $Opts->{expire_time}) ? parse_expire_time($Opts->{expire_time}) : -1; |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
# Hash value, lock page |
822
|
40885
|
|
|
|
|
77257
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
# If skip_lock is passed, it's a *reference* to an existing lock we |
825
|
|
|
|
|
|
|
# have to take and delete so we can cleanup below before calling |
826
|
|
|
|
|
|
|
# the callback |
827
|
40885
|
|
66
|
|
|
64945
|
my $Unlock = $Opts && $Opts->{skip_lock}; |
828
|
40885
|
100
|
|
|
|
46015
|
if ($Unlock) { |
829
|
15006
|
|
|
|
|
21149
|
($Unlock, $$Unlock) = ($$Unlock, undef); |
830
|
|
|
|
|
|
|
} else { |
831
|
25879
|
|
|
|
|
34287
|
$Unlock = $Self->_lock_page($HashPage); |
832
|
|
|
|
|
|
|
} |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
# Are we doing writeback's? If so, need to mark as dirty in cache |
835
|
40885
|
|
|
|
|
45172
|
my $write_back = $Self->{write_back}; |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
# Get key/value len (we've got 'use bytes'), and do expunge check to |
838
|
|
|
|
|
|
|
# create space if needed |
839
|
40885
|
100
|
|
|
|
64261
|
my $KVLen = length($_[1]) + (defined($Val) ? length($Val) : 0); |
840
|
40885
|
|
|
|
|
59719
|
$Self->_expunge_page(2, 1, $KVLen); |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
# Now store into cache |
843
|
40885
|
100
|
|
|
|
212087
|
my $DidStore = fc_write($Cache, $HashSlot, $_[1], $Val, $expire_seconds, $write_back ? FC_ISDIRTY : 0); |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
# Unlock page |
846
|
40885
|
|
|
|
|
34028
|
$Unlock = undef; |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
# If we're doing write-through, or write-back and didn't get into cache, |
849
|
|
|
|
|
|
|
# write back to the underlying store |
850
|
40885
|
100
|
66
|
|
|
68091
|
if ((!$write_back || !$DidStore) && (my $write_cb = $Self->{write_cb})) { |
|
|
|
100
|
|
|
|
|
851
|
3000
|
|
|
|
|
2322
|
eval { $write_cb->($Self->{context}, $_[1], $_[2]); }; |
|
3000
|
|
|
|
|
5507
|
|
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
|
854
|
40885
|
|
|
|
|
69650
|
return $DidStore; |
855
|
|
|
|
|
|
|
} |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
=item I |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
Atomically retrieve and set the value of a Key. |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
The page is locked while retrieving the $Key and is unlocked only after |
862
|
|
|
|
|
|
|
the value is set, thus guaranteeing the value does not change between |
863
|
|
|
|
|
|
|
the get and set operations. |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
$Sub is a reference to a subroutine that is called to calculate the |
866
|
|
|
|
|
|
|
new value to store. $Sub gets $Key and the current value |
867
|
|
|
|
|
|
|
as parameters, and |
868
|
|
|
|
|
|
|
should return the new value to set in the cache for the given $Key. |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
If the subroutine returns an empty list, no value is stored back |
871
|
|
|
|
|
|
|
in the cache. This avoids updating the expiry time on an entry |
872
|
|
|
|
|
|
|
if you want to do a "get if in cache, store if not present" type |
873
|
|
|
|
|
|
|
callback. |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
For example, to atomically increment a value in the cache, you |
876
|
|
|
|
|
|
|
can just use: |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
$Cache->get_and_set($Key, sub { return ++$_[1]; }); |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
In scalar context, the return value from this function is the *new* value |
881
|
|
|
|
|
|
|
stored back into the cache. |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
In list context, a two item array is returned; the new value stored |
884
|
|
|
|
|
|
|
back into the cache and a boolean that's true if the value was stored |
885
|
|
|
|
|
|
|
in the cache, false otherwise. See the PAGE SIZE AND KEY/VALUE LIMITS |
886
|
|
|
|
|
|
|
section for more details. |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
Notes: |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
=over 4 |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
=item * |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
Do not perform any get/set operations from the callback sub, as these |
895
|
|
|
|
|
|
|
operations lock the page and you may end up with a dead lock! |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
=item * |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
If your sub does a die/throws an exception, the page will correctly |
900
|
|
|
|
|
|
|
be unlocked (1.15 onwards) |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=back |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=cut |
905
|
|
|
|
|
|
|
sub get_and_set { |
906
|
15006
|
|
|
15006
|
1
|
87889
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
907
|
|
|
|
|
|
|
|
908
|
15006
|
|
|
|
|
35077
|
my ($Value, $Unlock) = $Self->get($_[1], { skip_unlock => 1 }); |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
# If this throws an error, $Unlock ref will still unlock page |
911
|
15006
|
|
|
|
|
38415
|
my @NewValue = $_[2]->($_[1], $Value); |
912
|
|
|
|
|
|
|
|
913
|
15006
|
|
|
|
|
49575
|
my $DidStore = 0; |
914
|
15006
|
50
|
|
|
|
25976
|
if (@NewValue) { |
915
|
15006
|
|
|
|
|
15213
|
($Value) = @NewValue; |
916
|
15006
|
|
|
|
|
45547
|
my $DidStore = $Self->set($_[1], $Value, { skip_lock => \$Unlock }); |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
|
919
|
15006
|
50
|
|
|
|
57696
|
return wantarray ? ($Value, $DidStore) : $Value; |
920
|
|
|
|
|
|
|
} |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
=item I |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
Delete the given key from the cache |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
I<%Options> is optional, and is used by get_and_remove() to control |
927
|
|
|
|
|
|
|
the locking behaviour. For now, you should probably ignore it |
928
|
|
|
|
|
|
|
unless you read the code to understand how it works |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
=cut |
931
|
|
|
|
|
|
|
sub remove { |
932
|
11301
|
|
|
11301
|
1
|
30468
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
# Hash value, lock page, read result |
935
|
11301
|
|
|
|
|
18968
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
# If skip_lock is passed, it's a *reference* to an existing lock we |
938
|
|
|
|
|
|
|
# have to take and delete so we can cleanup below before calling |
939
|
|
|
|
|
|
|
# the callback |
940
|
11301
|
|
66
|
|
|
20261
|
my $Unlock = $_[2] && $_[2]->{skip_lock}; |
941
|
11301
|
100
|
|
|
|
14618
|
if ($Unlock) { |
942
|
5000
|
|
|
|
|
6216
|
($Unlock, $$Unlock) = ($$Unlock, undef); |
943
|
|
|
|
|
|
|
} else { |
944
|
6301
|
|
|
|
|
7826
|
$Unlock = $Self->_lock_page($HashPage); |
945
|
|
|
|
|
|
|
} |
946
|
|
|
|
|
|
|
|
947
|
11301
|
|
|
|
|
26485
|
my ($DidDel, $Flags) = fc_delete($Cache, $HashSlot, $_[1]); |
948
|
11301
|
|
|
|
|
10490
|
$Unlock = undef; |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
# If we deleted from the cache, and it's not dirty, also delete |
951
|
|
|
|
|
|
|
# from underlying store |
952
|
11301
|
100
|
66
|
|
|
17060
|
if ((!$DidDel || ($DidDel && !($Flags & FC_ISDIRTY))) |
|
|
|
66
|
|
|
|
|
953
|
|
|
|
|
|
|
&& (my $delete_cb = $Self->{delete_cb})) { |
954
|
301
|
|
|
|
|
206
|
eval { $delete_cb->($Self->{context}, $_[1]); }; |
|
301
|
|
|
|
|
391
|
|
955
|
|
|
|
|
|
|
} |
956
|
|
|
|
|
|
|
|
957
|
11301
|
|
|
|
|
16390
|
return $DidDel; |
958
|
|
|
|
|
|
|
} |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
=item I |
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
Atomically retrieve value of a Key while removing it from the cache. |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
The page is locked while retrieving the $Key and is unlocked only after |
965
|
|
|
|
|
|
|
the value is removed, thus guaranteeing the value stored by someone else |
966
|
|
|
|
|
|
|
isn't removed by us. |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
=cut |
969
|
|
|
|
|
|
|
sub get_and_remove { |
970
|
5000
|
|
|
5000
|
1
|
14103
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
971
|
|
|
|
|
|
|
|
972
|
5000
|
|
|
|
|
12795
|
my ($Value, $Unlock) = $Self->get($_[1], { skip_unlock => 1 }); |
973
|
5000
|
|
|
|
|
15191
|
my $DidDel = $Self->remove($_[1], { skip_lock => \$Unlock }); |
974
|
5000
|
50
|
|
|
|
15905
|
return wantarray ? ($Value, $DidDel) : $Value; |
975
|
|
|
|
|
|
|
} |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
=item I |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
Clear all items from the cache |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
Note: If you're using callbacks, this has no effect |
982
|
|
|
|
|
|
|
on items in the underlying data store. No delete |
983
|
|
|
|
|
|
|
callbacks are made |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
=cut |
986
|
|
|
|
|
|
|
sub clear { |
987
|
3
|
|
|
3
|
1
|
1677
|
my $Self = shift; |
988
|
3
|
|
|
|
|
10
|
$Self->_expunge_all(1, 0); |
989
|
|
|
|
|
|
|
} |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
=item I |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
Clear all expired items from the cache |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
Note: If you're using callbacks, this has no effect |
996
|
|
|
|
|
|
|
on items in the underlying data store. No delete |
997
|
|
|
|
|
|
|
callbacks are made, and no write callbacks are made |
998
|
|
|
|
|
|
|
for the expired data |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
=cut |
1001
|
|
|
|
|
|
|
sub purge { |
1002
|
0
|
|
|
0
|
1
|
0
|
my $Self = shift; |
1003
|
0
|
|
|
|
|
0
|
$Self->_expunge_all(0, 0); |
1004
|
|
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
=item I |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
Empty all items from the cache, or if $OnlyExpired is |
1009
|
|
|
|
|
|
|
true, only expired items. |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
Note: If 'write_back' mode is enabled, any changed items |
1012
|
|
|
|
|
|
|
are written back to the underlying store. Expired items are |
1013
|
|
|
|
|
|
|
written back to the underlying store as well. |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
=cut |
1016
|
|
|
|
|
|
|
sub empty { |
1017
|
5
|
|
|
5
|
1
|
1276
|
my $Self = shift; |
1018
|
5
|
50
|
|
|
|
28
|
$Self->_expunge_all($_[0] ? 0 : 1, 1); |
1019
|
|
|
|
|
|
|
} |
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
=item I |
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
Get a list of keys/values held in the cache. May immediately be out of |
1024
|
|
|
|
|
|
|
date because of the shared access nature of the cache |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
If $Mode == 0, an array of keys is returned |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
If $Mode == 1, then an array of hashrefs, with 'key', |
1029
|
|
|
|
|
|
|
'last_access', 'expire_time' and 'flags' keys is returned |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
If $Mode == 2, then hashrefs also contain 'value' key |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
=cut |
1034
|
|
|
|
|
|
|
sub get_keys { |
1035
|
12
|
|
|
12
|
1
|
20259
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1036
|
|
|
|
|
|
|
|
1037
|
12
|
|
100
|
|
|
47
|
my $Mode = $_[1] || 0; |
1038
|
12
|
|
|
|
|
28
|
my ($Uncompress, $Deserialize) = @$Self{qw(uncompress deserialize)}; |
1039
|
|
|
|
|
|
|
|
1040
|
12
|
100
|
33
|
|
|
3851
|
return fc_get_keys($Cache, $Mode) |
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
1041
|
|
|
|
|
|
|
if $Mode <= 1 || ($Mode == 2 && !$Uncompress && !$Deserialize); |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
# If we're getting values as well, and they're not raw, unfreeze them |
1044
|
1
|
|
|
|
|
181
|
my @Details = fc_get_keys($Cache, 2); |
1045
|
|
|
|
|
|
|
|
1046
|
1
|
|
|
|
|
4
|
for (@Details) { |
1047
|
2
|
|
|
|
|
3
|
my $Val = $_->{value}; |
1048
|
2
|
50
|
|
|
|
7
|
if (defined $Val) { |
1049
|
2
|
50
|
|
|
|
3
|
$Val = $Uncompress->($Val) if $Uncompress; |
1050
|
2
|
50
|
|
|
|
4
|
$Val = ${$Deserialize->($Val)} if $Deserialize; |
|
2
|
|
|
|
|
4
|
|
1051
|
2
|
|
|
|
|
21
|
$_->{value} = $Val; |
1052
|
|
|
|
|
|
|
} |
1053
|
|
|
|
|
|
|
} |
1054
|
1
|
|
|
|
|
3
|
return @Details; |
1055
|
|
|
|
|
|
|
} |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
=item I |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
Returns a two value list of (nreads, nreadhits). This |
1060
|
|
|
|
|
|
|
only works if you passed enable_stats in the constructor |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
nreads is the total number of read attempts done on the |
1063
|
|
|
|
|
|
|
cache since it was created |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
nreadhits is the total number of read attempts done on |
1066
|
|
|
|
|
|
|
the cache since it was created that found the key/value |
1067
|
|
|
|
|
|
|
in the cache |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
If $Clear is true, the values are reset immediately after |
1070
|
|
|
|
|
|
|
they are retrieved |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
=cut |
1073
|
|
|
|
|
|
|
sub get_statistics { |
1074
|
3
|
|
|
3
|
1
|
3310
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1075
|
3
|
|
|
|
|
6
|
my $Clear = $_[1]; |
1076
|
|
|
|
|
|
|
|
1077
|
3
|
|
|
|
|
7
|
my ($NReads, $NReadHits) = (0, 0); |
1078
|
3
|
|
|
|
|
13
|
for (0 .. $Self->{num_pages}-1) { |
1079
|
267
|
|
|
|
|
534
|
my $Unlock = $Self->_lock_page($_); |
1080
|
267
|
|
|
|
|
582
|
my ($PNReads, $PNReadHits) = fc_get_page_details($Cache); |
1081
|
267
|
|
|
|
|
266
|
$NReads += $PNReads; |
1082
|
267
|
|
|
|
|
219
|
$NReadHits += $PNReadHits; |
1083
|
267
|
100
|
|
|
|
695
|
fc_reset_page_details($Cache) if $Clear; |
1084
|
267
|
|
|
|
|
598
|
$Unlock = undef; |
1085
|
|
|
|
|
|
|
} |
1086
|
3
|
|
|
|
|
13
|
return ($NReads, $NReadHits); |
1087
|
|
|
|
|
|
|
} |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
=item I |
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
The two multi_xxx routines act a bit differently to the |
1092
|
|
|
|
|
|
|
other routines. With the multi_get, you pass a separate |
1093
|
|
|
|
|
|
|
PageKey value and then multiple keys. The PageKey value |
1094
|
|
|
|
|
|
|
is hashed, and that page locked. Then that page is |
1095
|
|
|
|
|
|
|
searched for each key. It returns a hash ref of |
1096
|
|
|
|
|
|
|
Key => Value items found in that page in the cache. |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
The main advantage of this is just a speed one, if you |
1099
|
|
|
|
|
|
|
happen to need to search for a lot of items on each call. |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
For instance, say you have users and a bunch of pieces |
1102
|
|
|
|
|
|
|
of separate information for each user. On a particular |
1103
|
|
|
|
|
|
|
run, you need to retrieve a sub-set of that information |
1104
|
|
|
|
|
|
|
for a user. You could do lots of get() calls, or you |
1105
|
|
|
|
|
|
|
could use the 'username' as the page key, and just |
1106
|
|
|
|
|
|
|
use one multi_get() and multi_set() call instead. |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
A couple of things to note: |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
=over 4 |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=item 1. |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
This makes multi_get()/multi_set() and get()/set() |
1115
|
|
|
|
|
|
|
incompatible. Don't mix calls to the two, because |
1116
|
|
|
|
|
|
|
you won't find the data you're expecting |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
=item 2. |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
The writeback and callback modes of operation do |
1121
|
|
|
|
|
|
|
not work with multi_get()/multi_set(). Don't attempt |
1122
|
|
|
|
|
|
|
to use them together. |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
=back |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
=cut |
1127
|
|
|
|
|
|
|
sub multi_get { |
1128
|
2
|
|
|
2
|
1
|
553
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
# Hash value page key, lock page |
1131
|
2
|
|
|
|
|
6
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
1132
|
2
|
|
|
|
|
6
|
my $Unlock = $Self->_lock_page($HashPage); |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
# For each key to find |
1135
|
2
|
|
|
|
|
4
|
my ($Keys, %KVs) = ($_[2]); |
1136
|
2
|
|
|
|
|
5
|
for (@$Keys) { |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
# Hash key to get slot in this page and read |
1139
|
4
|
|
|
|
|
6
|
my $FinalKey = "$_[1]-$_"; |
1140
|
4
|
|
|
|
|
12
|
(undef, $HashSlot) = fc_hash($Cache, $FinalKey); |
1141
|
4
|
|
|
|
|
16
|
my ($Val, $Flags, $Found) = fc_read($Cache, $HashSlot, $FinalKey); |
1142
|
4
|
50
|
|
|
|
8
|
next unless $Found; |
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
# If not using raw values, use thaw() to turn data back into object |
1145
|
4
|
50
|
33
|
|
|
13
|
$Val = $Self->{uncompress}($Val) if defined($Val) && $Self->{compress}; |
1146
|
4
|
50
|
33
|
|
|
12
|
$Val = ${$Self->{deserialize}($Val)} if defined($Val) && $Self->{deserialize}; |
|
0
|
|
|
|
|
0
|
|
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
# Save to return |
1149
|
4
|
|
|
|
|
9
|
$KVs{$_} = $Val; |
1150
|
|
|
|
|
|
|
} |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
# Unlock page and return any found value |
1153
|
2
|
|
|
|
|
3
|
$Unlock = undef; |
1154
|
|
|
|
|
|
|
|
1155
|
2
|
|
|
|
|
4
|
return \%KVs; |
1156
|
|
|
|
|
|
|
} |
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
=item I $Value1, $Key2 => $Value2, ... }, [ \%Options ])> |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
Store specified key/value pair into cache |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
=cut |
1163
|
|
|
|
|
|
|
sub multi_set { |
1164
|
2
|
|
|
2
|
1
|
1273
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
# Get opts, make compatible with Cache::Cache interface |
1167
|
2
|
0
|
|
|
|
6
|
my $Opts = defined($_[3]) ? (ref($_[3]) ? $_[3] : { expire_time => $_[3] }) : undef; |
|
|
50
|
|
|
|
|
|
1168
|
2
|
50
|
33
|
|
|
7
|
my $expire_seconds = defined($Opts && $Opts->{expire_time}) ? parse_expire_time($Opts->{expire_time}) : -1; |
1169
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
# Hash page key value, lock page |
1171
|
2
|
|
|
|
|
7
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
1172
|
2
|
|
|
|
|
4
|
my $Unlock = $Self->_lock_page($HashPage); |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
# Loop over each key/value storing into this page |
1175
|
2
|
|
|
|
|
3
|
my $KVs = $_[2]; |
1176
|
2
|
|
|
|
|
17
|
while (my ($Key, $Val) = each %$KVs) { |
1177
|
|
|
|
|
|
|
|
1178
|
4
|
50
|
|
|
|
11
|
$Val = $Self->{serialize}(\$Val) if $Self->{serialize}; |
1179
|
4
|
50
|
|
|
|
10
|
$Val = $Self->{compress}($Val) if $Self->{compress}; |
1180
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
# Get key/value len (we've got 'use bytes'), and do expunge check to |
1182
|
|
|
|
|
|
|
# create space if needed |
1183
|
4
|
|
|
|
|
8
|
my $FinalKey = "$_[1]-$Key"; |
1184
|
4
|
|
|
|
|
38
|
my $KVLen = length($FinalKey) + length($Val); |
1185
|
4
|
|
|
|
|
10
|
$Self->_expunge_page(2, 1, $KVLen); |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
# Now hash key and store into page |
1188
|
4
|
|
|
|
|
10
|
(undef, $HashSlot) = fc_hash($Cache, $FinalKey); |
1189
|
4
|
|
|
|
|
21
|
my $DidStore = fc_write($Cache, $HashSlot, $FinalKey, $Val, $expire_seconds, 0); |
1190
|
|
|
|
|
|
|
} |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
# Unlock page |
1193
|
2
|
|
|
|
|
4
|
$Unlock = undef; |
1194
|
|
|
|
|
|
|
|
1195
|
2
|
|
|
|
|
5
|
return 1; |
1196
|
|
|
|
|
|
|
} |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
=back |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
=cut |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
=head1 INTERNAL METHODS |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
=over 4 |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
=cut |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
=item I<_expunge_all($Mode, $WB)> |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
Expunge all items from the cache |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
Expunged items (that have not expired) are written |
1213
|
|
|
|
|
|
|
back to the underlying store if write_back is enabled |
1214
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
=cut |
1216
|
|
|
|
|
|
|
sub _expunge_all { |
1217
|
8
|
|
|
8
|
|
25
|
my ($Self, $Cache, $Mode, $WB) = ($_[0], $_[0]->{Cache}, $_[1], $_[2]); |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
# Repeat expunge for each page |
1220
|
8
|
|
|
|
|
35
|
for (0 .. $Self->{num_pages}-1) { |
1221
|
540
|
|
|
|
|
658
|
my $Unlock = $Self->_lock_page($_); |
1222
|
540
|
|
|
|
|
712
|
$Self->_expunge_page($Mode, $WB, -1); |
1223
|
540
|
|
|
|
|
1123
|
$Unlock = undef; |
1224
|
|
|
|
|
|
|
} |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
} |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
=item I<_expunge_page($Mode, $WB, $Len)> |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
Expunge items from the current page to make space for |
1231
|
|
|
|
|
|
|
$Len bytes key/value items |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
Expunged items (that have not expired) are written |
1234
|
|
|
|
|
|
|
back to the underlying store if write_back is enabled |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
=cut |
1237
|
|
|
|
|
|
|
sub _expunge_page { |
1238
|
54544
|
|
|
54544
|
|
86701
|
my ($Self, $Cache, $Mode, $WB, $Len) = ($_[0], $_[0]->{Cache}, @_[1 .. 3]); |
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
# If writeback mode, need to get expunged items to write back |
1241
|
54544
|
100
|
100
|
|
|
117007
|
my $write_cb = $Self->{write_back} && $WB ? $Self->{write_cb} : undef; |
1242
|
|
|
|
|
|
|
|
1243
|
54544
|
100
|
|
|
|
171481
|
my @WBItems = fc_expunge($Cache, $Mode, $write_cb ? 1 : 0, $Len); |
1244
|
|
|
|
|
|
|
|
1245
|
54544
|
|
|
|
|
64748
|
my ($Uncompress, $Deserialize) = @$Self{qw(uncompress deserialize)}; |
1246
|
|
|
|
|
|
|
|
1247
|
54544
|
|
|
|
|
84949
|
for (@WBItems) { |
1248
|
14128
|
100
|
|
|
|
32253
|
next if !($_->{flags} & FC_ISDIRTY); |
1249
|
|
|
|
|
|
|
|
1250
|
6104
|
|
|
|
|
4560
|
my $Val = $_->{value}; |
1251
|
6104
|
100
|
|
|
|
7162
|
if (defined $Val) { |
1252
|
6103
|
50
|
|
|
|
6855
|
$Val = $Uncompress->($Val) if $Uncompress; |
1253
|
6103
|
100
|
|
|
|
6700
|
$Val = ${$Deserialize->($Val)} if $Deserialize; |
|
2
|
|
|
|
|
5
|
|
1254
|
|
|
|
|
|
|
} |
1255
|
6104
|
|
|
|
|
4159
|
eval { $write_cb->($Self->{context}, $_->{key}, $Val, $_->{expire_time}); }; |
|
6104
|
|
|
|
|
8448
|
|
1256
|
|
|
|
|
|
|
} |
1257
|
|
|
|
|
|
|
} |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
=item I<_lock_page($Page)> |
1260
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
Lock a given page in the cache, and return an object |
1262
|
|
|
|
|
|
|
reference that when DESTROYed, unlocks the page |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
=cut |
1265
|
|
|
|
|
|
|
sub _lock_page { |
1266
|
198767
|
|
|
198767
|
|
169419
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1267
|
|
|
|
|
|
|
my $Unlock = Cache::FastMmap::OnLeave->new(sub { |
1268
|
198767
|
100
|
|
198767
|
|
1132133
|
fc_unlock($Cache) if fc_is_locked($Cache); |
1269
|
198767
|
|
|
|
|
518656
|
}); |
1270
|
198767
|
|
|
|
|
2080301
|
fc_lock($Cache, $_[1]); |
1271
|
198766
|
|
|
|
|
202565
|
return $Unlock; |
1272
|
|
|
|
|
|
|
} |
1273
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
sub parse_expire_time { |
1275
|
42
|
|
100
|
42
|
0
|
223
|
my $expire_time = shift || ''; |
1276
|
42
|
100
|
|
|
|
117
|
return 1 if $expire_time eq 'now'; |
1277
|
40
|
100
|
|
|
|
169
|
return 0 if $expire_time eq 'never'; |
1278
|
38
|
|
|
|
|
198
|
my %Times = ('' => 1, s => 1, m => 60, h => 60*60, d => 24*60*60, w => 7*24*60*60); |
1279
|
38
|
100
|
|
|
|
273
|
return $expire_time =~ /^(\d+)\s*([mhdws]?)/i ? $1 * $Times{lc($2)} : 0; |
1280
|
|
|
|
|
|
|
} |
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
sub cleanup { |
1283
|
23
|
|
|
23
|
0
|
75
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
# Avoid potential double cleanup |
1286
|
23
|
50
|
|
|
|
113
|
return if $Self->{cleaned}; |
1287
|
23
|
|
|
|
|
88
|
$Self->{cleaned} = 1; |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
# Expunge all entries on exit if requested and in parent process |
1290
|
23
|
50
|
66
|
|
|
152
|
if ($Self->{empty_on_exit} && $Cache && $Self->{pid} == $$) { |
|
|
|
66
|
|
|
|
|
1291
|
1
|
|
|
|
|
4
|
$Self->empty(); |
1292
|
|
|
|
|
|
|
} |
1293
|
|
|
|
|
|
|
|
1294
|
23
|
50
|
|
|
|
141
|
if ($Cache) { |
1295
|
23
|
|
|
|
|
1323
|
fc_close($Cache); |
1296
|
23
|
|
|
|
|
45
|
$Cache = undef; |
1297
|
23
|
|
|
|
|
93
|
delete $Self->{Cache}; |
1298
|
|
|
|
|
|
|
} |
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
unlink($Self->{share_file}) |
1301
|
23
|
100
|
66
|
|
|
15983
|
if $Self->{unlink_on_exit} && $Self->{pid} == $$; |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
} |
1304
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
sub DESTROY { |
1306
|
23
|
|
|
23
|
|
110794
|
my $Self = shift; |
1307
|
23
|
|
|
|
|
146
|
$Self->cleanup(); |
1308
|
23
|
100
|
|
|
|
2834
|
delete $LiveCaches{ref($Self)} if $Self->{empty_on_exit}; |
1309
|
|
|
|
|
|
|
} |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
sub END { |
1312
|
17
|
|
|
17
|
|
21338
|
while (my (undef, $Self) = each %LiveCaches) { |
1313
|
|
|
|
|
|
|
# Weak reference, might be undef already |
1314
|
0
|
0
|
|
|
|
0
|
$Self->cleanup() if $Self; |
1315
|
|
|
|
|
|
|
} |
1316
|
17
|
|
|
|
|
9273
|
%LiveCaches = (); |
1317
|
|
|
|
|
|
|
} |
1318
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
sub CLONE { |
1320
|
0
|
|
|
0
|
|
0
|
die "Cache::FastMmap does not support threads sorry"; |
1321
|
|
|
|
|
|
|
} |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
1; |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
package Cache::FastMmap::OnLeave; |
1326
|
17
|
|
|
17
|
|
137
|
use strict; |
|
17
|
|
|
|
|
34
|
|
|
17
|
|
|
|
|
2230
|
|
1327
|
|
|
|
|
|
|
|
1328
|
|
|
|
|
|
|
sub new { |
1329
|
198767
|
|
|
198767
|
|
178831
|
my $Class = shift; |
1330
|
198767
|
|
|
|
|
148335
|
my $Ref = \$_[0]; |
1331
|
198767
|
|
|
|
|
200553
|
bless $Ref, $Class; |
1332
|
198767
|
|
|
|
|
205141
|
return $Ref; |
1333
|
|
|
|
|
|
|
} |
1334
|
|
|
|
|
|
|
|
1335
|
|
|
|
|
|
|
sub disable { |
1336
|
0
|
|
|
0
|
|
0
|
${$_[0]} = undef; |
|
0
|
|
|
|
|
0
|
|
1337
|
|
|
|
|
|
|
} |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
sub DESTROY { |
1340
|
198767
|
|
|
198767
|
|
155131
|
my $e = $@; # Save errors from code calling us |
1341
|
198767
|
|
|
|
|
141230
|
eval { |
1342
|
|
|
|
|
|
|
|
1343
|
198767
|
|
|
|
|
144846
|
my $Ref = shift; |
1344
|
198767
|
50
|
|
|
|
351020
|
$$Ref->() if $$Ref; |
1345
|
|
|
|
|
|
|
|
1346
|
|
|
|
|
|
|
}; |
1347
|
|
|
|
|
|
|
# $e .= " (in cleanup) $@" if $@; |
1348
|
198767
|
|
|
|
|
923603
|
$@ = $e; |
1349
|
|
|
|
|
|
|
} |
1350
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
1; |
1352
|
|
|
|
|
|
|
|
1353
|
|
|
|
|
|
|
__END__ |