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
|
18
|
|
|
18
|
|
322537
|
use 5.006; |
|
18
|
|
|
|
|
80
|
|
292
|
18
|
|
|
18
|
|
102
|
use strict; |
|
18
|
|
|
|
|
66
|
|
|
18
|
|
|
|
|
393
|
|
293
|
18
|
|
|
18
|
|
91
|
use warnings; |
|
18
|
|
|
|
|
48
|
|
|
18
|
|
|
|
|
617
|
|
294
|
18
|
|
|
18
|
|
9677
|
use bytes; |
|
18
|
|
|
|
|
228
|
|
|
18
|
|
|
|
|
91
|
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
our $VERSION = '1.46'; |
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
|
18
|
|
|
18
|
|
1236
|
use constant FC_ISDIRTY => 1; |
|
18
|
|
|
|
|
40
|
|
|
18
|
|
|
|
|
1365
|
|
306
|
|
|
|
|
|
|
|
307
|
18
|
|
|
18
|
|
112
|
use File::Spec; |
|
18
|
|
|
|
|
33
|
|
|
18
|
|
|
|
|
58501
|
|
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# }}} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=item I |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
Create a new Cache::FastMmap object. |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
Basic global parameters are: |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=over 4 |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=item * B |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
File to mmap for sharing of data. |
322
|
|
|
|
|
|
|
default on unix: /tmp/sharefile-$pid-$time-$random |
323
|
|
|
|
|
|
|
default on windows: %TEMP%\sharefile-$pid-$time-$random |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=item * B |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
Clear any existing values and re-initialise file. Useful to do in a |
328
|
|
|
|
|
|
|
parent that forks off children to ensure that file is empty at the start |
329
|
|
|
|
|
|
|
(default: 0) |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
B This is quite important to do in the parent to ensure a |
332
|
|
|
|
|
|
|
consistent file structure. The shared file is not perfectly transaction |
333
|
|
|
|
|
|
|
safe, and so if a child is killed at the wrong instant, it might leave |
334
|
|
|
|
|
|
|
the cache file in an inconsistent state. |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=item * B |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Use a serialization library to serialize perl data structures before |
339
|
|
|
|
|
|
|
storing in the cache. If not set, the raw value in the variable passed |
340
|
|
|
|
|
|
|
to set() is stored as a string. You must set this if you want to store |
341
|
|
|
|
|
|
|
anything other than basic scalar values. Supported values are: |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
'' for none |
344
|
|
|
|
|
|
|
'storable' for 'Storable' |
345
|
|
|
|
|
|
|
'sereal' for 'Sereal' |
346
|
|
|
|
|
|
|
'json' for 'JSON' |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
If this parameter has a value the module will attempt to load the |
349
|
|
|
|
|
|
|
associated package and then use the API of that package to serialize data |
350
|
|
|
|
|
|
|
before storing in the cache, and deserialize it upon retrieval from the |
351
|
|
|
|
|
|
|
cache. (default: 'storable') |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
(Note: Historically this module only supported a boolean value for the |
354
|
|
|
|
|
|
|
`raw_values` parameter and defaulted to 0, which meant it used Storable |
355
|
|
|
|
|
|
|
to serialze all values.) |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=item * B |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
Deprecated. Use B above |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=item * B |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Compress the value (but not the key) before storing into the cache, using |
364
|
|
|
|
|
|
|
the compression package identified by the value of the parameter. Supported |
365
|
|
|
|
|
|
|
values are: |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
'zlib' for 'Compress::Zlib' |
368
|
|
|
|
|
|
|
'lz4' for 'Compress::LZ4' |
369
|
|
|
|
|
|
|
'snappy' for 'Compress::Snappy' |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
If this parameter has a value the module will attempt to load the |
372
|
|
|
|
|
|
|
associated package and then use the API of that package to compress data |
373
|
|
|
|
|
|
|
before storing in the cache, and uncompress it upon retrieval from the |
374
|
|
|
|
|
|
|
cache. (default: undef) |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
(Note: Historically this module only supported a boolean value for the |
377
|
|
|
|
|
|
|
`compress` parameter and defaulted to use Compress::Zlib. The note for the |
378
|
|
|
|
|
|
|
old `compress` parameter stated: "Some initial testing shows that the |
379
|
|
|
|
|
|
|
uncompressing tends to be very fast, though the compressing can be quite |
380
|
|
|
|
|
|
|
slow, so it's probably best to use this option only if you know values in |
381
|
|
|
|
|
|
|
the cache are long-lived and have a high hit rate." |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
Comparable test results for the other compression tools are not yet available; |
384
|
|
|
|
|
|
|
submission of benchmarks welcome. However, the documentation for the 'Snappy' |
385
|
|
|
|
|
|
|
library (http://google.github.io/snappy/) states: For instance, compared to |
386
|
|
|
|
|
|
|
the fastest mode of zlib, Snappy is an order of magnitude faster for most |
387
|
|
|
|
|
|
|
inputs, but the resulting compressed files are anywhere from 20% to 100% |
388
|
|
|
|
|
|
|
bigger. ) |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=item * B |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
Deprecated. Please use B, see above. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=item * B |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
Enable some basic statistics capturing. When enabled, every read to |
397
|
|
|
|
|
|
|
the cache is counted, and every read to the cache that finds a value |
398
|
|
|
|
|
|
|
in the cache is also counted. You can then retrieve these values |
399
|
|
|
|
|
|
|
via the get_statistics() call. This causes every read action to |
400
|
|
|
|
|
|
|
do a write on a page, which can cause some more IO, so it's |
401
|
|
|
|
|
|
|
disabled by default. (default: 0) |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
=item * B |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
Maximum time to hold values in the cache in seconds. A value of 0 |
406
|
|
|
|
|
|
|
means does no explicit expiry time, and values are expired only based |
407
|
|
|
|
|
|
|
on LRU usage. Can be expressed as 1m, 1h, 1d for minutes/hours/days |
408
|
|
|
|
|
|
|
respectively. (default: 0) |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=back |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
You may specify the cache size as: |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=over 4 |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
=item * B |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
Size of cache. Can be expresses as 1k, 1m for kilobytes or megabytes |
419
|
|
|
|
|
|
|
respectively. Automatically guesses page size/page count values. |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
=back |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
Or specify explicit page size/page count values. If none of these are |
424
|
|
|
|
|
|
|
specified, the values page_size = 64k and num_pages = 89 are used. |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
=over 4 |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=item * B |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
Size of each page. Must be a power of 2 between 4k and 1024k. If not, |
431
|
|
|
|
|
|
|
is rounded to the nearest value. |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=item * B |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Number of pages. Should be a prime number for best hashing |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=back |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
The cache allows the use of callbacks for reading/writing data to an |
440
|
|
|
|
|
|
|
underlying data store. |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=over 4 |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=item * B |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
Opaque reference passed as the first parameter to any callback function |
447
|
|
|
|
|
|
|
if specified |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=item * B |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
Callback to read data from the underlying data store. Called as: |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
$read_cb->($context, $Key) |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
Should return the value to use. This value will be saved in the cache |
456
|
|
|
|
|
|
|
for future retrievals. Return undef if there is no value for the |
457
|
|
|
|
|
|
|
given key |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=item * B |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
Callback to write data to the underlying data store. |
462
|
|
|
|
|
|
|
Called as: |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
$write_cb->($context, $Key, $Value, $ExpiryTime) |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
In 'write_through' mode, it's always called as soon as a I |
467
|
|
|
|
|
|
|
is called on the Cache::FastMmap class. In 'write_back' mode, it's |
468
|
|
|
|
|
|
|
called when a value is expunged from the cache if it's been changed |
469
|
|
|
|
|
|
|
by a I rather than read from the underlying store with the |
470
|
|
|
|
|
|
|
I above. |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
Note: Expired items do result in the I being |
473
|
|
|
|
|
|
|
called if 'write_back' caching is enabled and the item has been |
474
|
|
|
|
|
|
|
changed. You can check the $ExpiryTime against C |
475
|
|
|
|
|
|
|
want to write back values which aren't expired. |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
Also remember that I may be called in a different process |
478
|
|
|
|
|
|
|
to the one that placed the data in the cache in the first place |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=item * B |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
Callback to delete data from the underlying data store. Called as: |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
$delete_cb->($context, $Key) |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
Called as soon as I is called on the Cache::FastMmap class |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=item * B |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
If set to true, then if the I is called and it returns |
491
|
|
|
|
|
|
|
undef to say nothing was found, then that information is stored |
492
|
|
|
|
|
|
|
in the cache, so that next time a I is called on that |
493
|
|
|
|
|
|
|
key, undef is returned immediately rather than again calling |
494
|
|
|
|
|
|
|
the I |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=item * B |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
Either 'write_back' or 'write_through'. (default: write_through) |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=item * B |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
If you're using a callback function, then normally the cache is not |
503
|
|
|
|
|
|
|
re-enterable, and attempting to call a get/set on the cache will |
504
|
|
|
|
|
|
|
cause an error. By setting this to one, the cache will unlock any |
505
|
|
|
|
|
|
|
pages before calling the callback. During the unlock time, other |
506
|
|
|
|
|
|
|
processes may change data in current cache page, causing possible |
507
|
|
|
|
|
|
|
unexpected effects. You shouldn't set this unless you know you |
508
|
|
|
|
|
|
|
want to be able to recall to the cache within a callback. |
509
|
|
|
|
|
|
|
(default: 0) |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=item * B |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
When you have 'write_back' mode enabled, then |
514
|
|
|
|
|
|
|
you really want to make sure all values from the cache are expunged |
515
|
|
|
|
|
|
|
when your program exits so any changes are written back. |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
The trick is that we only want to do this in the parent process, |
518
|
|
|
|
|
|
|
we don't want any child processes to empty the cache when they exit. |
519
|
|
|
|
|
|
|
So if you set this, it takes the PID via $$, and only calls |
520
|
|
|
|
|
|
|
empty in the DESTROY method if $$ matches the pid we captured |
521
|
|
|
|
|
|
|
at the start. (default: 0) |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=item * B |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
Unlink the share file when the cache is destroyed. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
As with empty_on_exit, this will only unlink the file if the |
528
|
|
|
|
|
|
|
DESTROY occurs in the same PID that the cache was created in |
529
|
|
|
|
|
|
|
so that any forked children don't unlink the file. |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
This value defaults to 1 if the share_file specified does |
532
|
|
|
|
|
|
|
not already exist. If the share_file specified does already |
533
|
|
|
|
|
|
|
exist, it defaults to 0. |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
=item * B |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
Sets an alarm(10) before each page is locked via fcntl(F_SETLKW) to catch |
538
|
|
|
|
|
|
|
any deadlock. This used to be the default behaviour, but it's not really |
539
|
|
|
|
|
|
|
needed in the default case and could clobber sub-second Time::HiRes |
540
|
|
|
|
|
|
|
alarms setup by other code. Defaults to 0. |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=back |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
=cut |
545
|
|
|
|
|
|
|
sub new { |
546
|
8027
|
|
|
8027
|
1
|
154364
|
my $Proto = shift; |
547
|
8027
|
|
33
|
|
|
32555
|
my $Class = ref($Proto) || $Proto; |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
# If first item is a hash ref, use it as arguments |
550
|
8027
|
50
|
|
|
|
45413
|
my %Args = ref($_[0]) eq 'HASH' ? %{shift()} : @_; |
|
0
|
|
|
|
|
0
|
|
551
|
|
|
|
|
|
|
|
552
|
8027
|
|
|
|
|
17035
|
my $Self = {}; |
553
|
8027
|
|
|
|
|
16362
|
bless ($Self, $Class); |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
# Work out cache file and whether to init |
556
|
8027
|
|
|
|
|
14940
|
my $share_file = $Args{share_file}; |
557
|
8027
|
50
|
|
|
|
19658
|
if (!$share_file) { |
558
|
8027
|
|
|
|
|
84630
|
my $tmp_dir = File::Spec->tmpdir; |
559
|
8027
|
|
|
|
|
70613
|
$share_file = File::Spec->catfile($tmp_dir, "sharefile"); |
560
|
8027
|
|
|
|
|
42732
|
$share_file .= "-" . $$ . "-" . time . "-" . int(rand(100000)); |
561
|
|
|
|
|
|
|
} |
562
|
8027
|
50
|
|
|
|
22103
|
!ref($share_file) || die "share_file argument was a reference"; |
563
|
8027
|
|
|
|
|
19277
|
$Self->{share_file} = $share_file; |
564
|
8027
|
|
|
|
|
14207
|
my $permissions = $Args{permissions}; |
565
|
|
|
|
|
|
|
|
566
|
8027
|
100
|
|
|
|
18585
|
my $init_file = $Args{init_file} ? 1 : 0; |
567
|
8027
|
50
|
|
|
|
21515
|
my $test_file = $Args{test_file} ? 1 : 0; |
568
|
8027
|
100
|
|
|
|
16452
|
my $enable_stats = $Args{enable_stats} ? 1 : 0; |
569
|
8027
|
50
|
|
|
|
15290
|
my $catch_deadlocks = $Args{catch_deadlocks} ? 1 : 0; |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
# Worth out unlink default if not specified |
572
|
8027
|
50
|
|
|
|
18597
|
if (!exists $Args{unlink_on_exit}) { |
573
|
8027
|
50
|
|
|
|
374994
|
$Args{unlink_on_exit} = -f($share_file) ? 0 : 1; |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
# Serialise stored values? |
577
|
8027
|
50
|
66
|
|
|
27902
|
my $serializer = $Args{serializer} // ($Args{raw_values} ? '' : 'storable'); |
578
|
|
|
|
|
|
|
|
579
|
8027
|
100
|
|
|
|
17875
|
if ($serializer) { |
580
|
5
|
50
|
|
|
|
25
|
if ($serializer eq 'storable') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
581
|
5
|
50
|
|
|
|
343
|
eval "require Storable;" |
582
|
|
|
|
|
|
|
|| die "Could not load serialization package: Storable : $@"; |
583
|
5
|
|
|
|
|
11445
|
$Self->{serialize} = Storable->can("freeze"); |
584
|
5
|
|
|
|
|
31
|
$Self->{deserialize} = Storable->can("thaw"); |
585
|
|
|
|
|
|
|
} elsif ($serializer eq 'sereal') { |
586
|
0
|
0
|
|
|
|
0
|
eval "require Sereal::Encoder; require Sereal::Decoder;" |
587
|
|
|
|
|
|
|
|| die "Could not load serialization package: Sereal : $@"; |
588
|
0
|
|
|
|
|
0
|
my $SerealEnc = Sereal::Encoder->new(); |
589
|
0
|
|
|
|
|
0
|
my $SerealDec = Sereal::Decoder->new(); |
590
|
0
|
|
|
0
|
|
0
|
$Self->{serialize} = sub { $SerealEnc->encode(@_); }; |
|
0
|
|
|
|
|
0
|
|
591
|
0
|
|
|
0
|
|
0
|
$Self->{deserialize} = sub { $SerealDec->decode(@_); }; |
|
0
|
|
|
|
|
0
|
|
592
|
|
|
|
|
|
|
} elsif ($serializer eq 'json') { |
593
|
0
|
0
|
|
|
|
0
|
eval "require JSON;" |
594
|
|
|
|
|
|
|
|| die "Could not load serialization package: JSON : $@"; |
595
|
0
|
|
|
|
|
0
|
my $JSON = JSON->new->utf8->allow_nonref; |
596
|
0
|
|
|
0
|
|
0
|
$Self->{serialize} = sub { $JSON->encode(${$_[0]}); }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
597
|
0
|
|
|
0
|
|
0
|
$Self->{deserialize} = sub { \$JSON->decode($_[0]); }; |
|
0
|
|
|
|
|
0
|
|
598
|
|
|
|
|
|
|
} else { |
599
|
0
|
|
|
|
|
0
|
die "Unrecognized value >$serializer< for `serializer` parameter"; |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
# Compress stored values? |
604
|
8027
|
100
|
100
|
|
|
35583
|
my $compressor = $Args{compressor} // ($Args{compress} ? 'zlib' : 0); |
605
|
|
|
|
|
|
|
|
606
|
8027
|
|
|
|
|
32686
|
my %known_compressors = ( |
607
|
|
|
|
|
|
|
zlib => 'Compress::Zlib', |
608
|
|
|
|
|
|
|
lz4 => 'Compress::LZ4', |
609
|
|
|
|
|
|
|
snappy => 'Compress::Snappy', |
610
|
|
|
|
|
|
|
); |
611
|
|
|
|
|
|
|
|
612
|
8027
|
100
|
|
|
|
18544
|
if ( $compressor ) { |
613
|
4
|
|
50
|
|
|
17
|
my $compressor_module = $known_compressors{$compressor} |
614
|
|
|
|
|
|
|
|| die "Unrecognized value >$compressor< for `compressor` parameter"; |
615
|
|
|
|
|
|
|
|
616
|
4
|
50
|
|
|
|
271
|
if ( ! eval "require $compressor_module;" ) { |
617
|
0
|
|
|
|
|
0
|
die "Could not load compression package: $compressor_module : $@"; |
618
|
|
|
|
|
|
|
} else { |
619
|
|
|
|
|
|
|
# LZ4 and Snappy use same API |
620
|
4
|
50
|
33
|
|
|
42
|
if ($compressor_module eq 'Compress::LZ4' || $compressor_module eq 'Compress::Snappy') { |
|
|
50
|
|
|
|
|
|
621
|
0
|
|
|
|
|
0
|
$Self->{compress} = $compressor_module->can("compress"); |
622
|
0
|
|
|
|
|
0
|
$Self->{uncompress} = $compressor_module->can("uncompress"); |
623
|
|
|
|
|
|
|
} elsif ($compressor_module eq 'Compress::Zlib') { |
624
|
4
|
|
|
|
|
36
|
$Self->{compress} = $compressor_module->can("memGzip"); |
625
|
|
|
|
|
|
|
# (gunzip from tmp var: https://rt.cpan.org/Ticket/Display.html?id=72945) |
626
|
4
|
|
|
|
|
23
|
my $uncompress = $compressor_module->can("memGunzip"); |
627
|
4
|
|
|
2
|
|
23
|
$Self->{uncompress} = sub { &$uncompress(my $Tmp = shift) }; |
|
2
|
|
|
|
|
11
|
|
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
# If using empty_on_exit, need to track used caches |
633
|
8027
|
|
100
|
|
|
30202
|
my $empty_on_exit = $Self->{empty_on_exit} = int($Args{empty_on_exit} || 0); |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
# Need Scalar::Util::weaken to track open caches |
636
|
8027
|
100
|
|
|
|
18884
|
if ($empty_on_exit) { |
637
|
1
|
50
|
|
1
|
|
63
|
eval "use Scalar::Util qw(weaken); 1;" |
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
46
|
|
638
|
|
|
|
|
|
|
|| die "Could not load Scalar::Util module: $@"; |
639
|
|
|
|
|
|
|
} |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# Work out expiry time in seconds |
642
|
8027
|
|
|
|
|
26566
|
my $expire_time = $Self->{expire_time} = parse_expire_time($Args{expire_time}); |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
# Function rounds to the nearest power of 2 |
645
|
8027
|
|
|
8027
|
0
|
30861
|
sub RoundPow2 { return int(2 ** int(log($_[0])/log(2)) + 0.1); } |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
# Work out cache size |
648
|
8027
|
|
|
|
|
20790
|
my ($cache_size, $num_pages, $page_size); |
649
|
|
|
|
|
|
|
|
650
|
8027
|
|
|
|
|
18772
|
my %Sizes = (k => 1024, m => 1024*1024); |
651
|
8027
|
50
|
|
|
|
17089
|
if ($cache_size = $Args{cache_size}) { |
652
|
0
|
0
|
|
|
|
0
|
$cache_size *= $Sizes{lc($1)} if $cache_size =~ s/([km])$//i; |
653
|
|
|
|
|
|
|
|
654
|
0
|
0
|
|
|
|
0
|
if ($num_pages = $Args{num_pages}) { |
655
|
0
|
|
|
|
|
0
|
$page_size = RoundPow2($cache_size / $num_pages); |
656
|
0
|
0
|
|
|
|
0
|
$page_size = 4096 if $page_size < 4096; |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
} else { |
659
|
0
|
|
0
|
|
|
0
|
$page_size = $Args{page_size} || 65536; |
660
|
0
|
0
|
|
|
|
0
|
$page_size *= $Sizes{lc($1)} if $page_size =~ s/([km])$//i; |
661
|
0
|
0
|
|
|
|
0
|
$page_size = 4096 if $page_size < 4096; |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
# Increase num_pages till we exceed |
664
|
0
|
|
|
|
|
0
|
$num_pages = 89; |
665
|
0
|
0
|
|
|
|
0
|
if ($num_pages * $page_size <= $cache_size) { |
666
|
0
|
|
|
|
|
0
|
while ($num_pages * $page_size <= $cache_size) { |
667
|
0
|
|
|
|
|
0
|
$num_pages = $num_pages * 2 + 1; |
668
|
|
|
|
|
|
|
} |
669
|
|
|
|
|
|
|
} else { |
670
|
0
|
|
|
|
|
0
|
while ($num_pages * $page_size > $cache_size) { |
671
|
0
|
|
|
|
|
0
|
$num_pages = int(($num_pages-1) / 2); |
672
|
|
|
|
|
|
|
} |
673
|
0
|
|
|
|
|
0
|
$num_pages = $num_pages * 2 + 1; |
674
|
|
|
|
|
|
|
} |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
} |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
} else { |
679
|
8027
|
|
|
|
|
18162
|
($num_pages, $page_size) = @Args{qw(num_pages page_size)}; |
680
|
8027
|
|
100
|
|
|
18942
|
$num_pages ||= 89; |
681
|
8027
|
|
100
|
|
|
17315
|
$page_size ||= 65536; |
682
|
8027
|
50
|
|
|
|
36126
|
$page_size *= $Sizes{lc($1)} if $page_size =~ s/([km])$//i; |
683
|
8027
|
|
|
|
|
18991
|
$page_size = RoundPow2($page_size); |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
|
686
|
8027
|
|
|
|
|
14703
|
$cache_size = $num_pages * $page_size; |
687
|
8027
|
|
|
|
|
24320
|
@$Self{qw(cache_size num_pages page_size)} |
688
|
|
|
|
|
|
|
= ($cache_size, $num_pages, $page_size); |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
# Number of slots to start in each page |
691
|
8027
|
|
50
|
|
|
42792
|
my $start_slots = int($Args{start_slots} || 0) || 89; |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
# Save read through/write back/write through details |
694
|
8027
|
|
100
|
|
|
22590
|
my $write_back = ($Args{write_action} || 'write_through') eq 'write_back'; |
695
|
|
|
|
|
|
|
@$Self{qw(context read_cb write_cb delete_cb)} |
696
|
8027
|
|
|
|
|
28839
|
= @Args{qw(context read_cb write_cb delete_cb)}; |
697
|
|
|
|
|
|
|
@$Self{qw(cache_not_found allow_recursive write_back)} |
698
|
8027
|
|
|
|
|
21131
|
= (@Args{qw(cache_not_found allow_recursive)}, $write_back); |
699
|
|
|
|
|
|
|
@$Self{qw(unlink_on_exit enable_stats)} |
700
|
8027
|
|
|
|
|
18801
|
= (@Args{qw(unlink_on_exit)}, $enable_stats); |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
# Save pid |
703
|
8027
|
|
|
|
|
26776
|
$Self->{pid} = $$; |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
# Initialise C cache code |
706
|
8027
|
|
|
|
|
23797
|
my $Cache = fc_new(); |
707
|
|
|
|
|
|
|
|
708
|
8027
|
|
|
|
|
15160
|
$Self->{Cache} = $Cache; |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
# Setup cache parameters |
711
|
8027
|
|
|
|
|
28046
|
fc_set_param($Cache, 'init_file', $init_file); |
712
|
8027
|
|
|
|
|
19586
|
fc_set_param($Cache, 'test_file', $test_file); |
713
|
8027
|
|
|
|
|
20699
|
fc_set_param($Cache, 'page_size', $page_size); |
714
|
8027
|
|
|
|
|
19589
|
fc_set_param($Cache, 'num_pages', $num_pages); |
715
|
8027
|
|
|
|
|
18453
|
fc_set_param($Cache, 'expire_time', $expire_time); |
716
|
8027
|
|
|
|
|
18673
|
fc_set_param($Cache, 'share_file', $share_file); |
717
|
8027
|
50
|
|
|
|
18557
|
fc_set_param($Cache, 'permissions', $permissions) if defined $permissions; |
718
|
8027
|
|
|
|
|
20331
|
fc_set_param($Cache, 'start_slots', $start_slots); |
719
|
8027
|
|
|
|
|
18883
|
fc_set_param($Cache, 'catch_deadlocks', $catch_deadlocks); |
720
|
8027
|
|
|
|
|
19212
|
fc_set_param($Cache, 'enable_stats', $enable_stats); |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
# And initialise it |
723
|
8027
|
|
|
|
|
3065114
|
fc_init($Cache); |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
# Track cache if need to empty on exit |
726
|
8027
|
100
|
|
|
|
40171
|
weaken($LiveCaches{ref($Self)} = $Self) |
727
|
|
|
|
|
|
|
if $empty_on_exit; |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
# All done, return PERL hash ref as class |
730
|
8027
|
|
|
|
|
73500
|
return $Self; |
731
|
|
|
|
|
|
|
} |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
=item I |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Search cache for given Key. Returns undef if not found. If |
736
|
|
|
|
|
|
|
I specified and not found, calls the callback to try |
737
|
|
|
|
|
|
|
and find the value for the key, and if found (or 'cache_not_found' |
738
|
|
|
|
|
|
|
is set), stores it into the cache and returns the found value. |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
I<%Options> is optional, and is used by get_and_set() to control |
741
|
|
|
|
|
|
|
the locking behaviour. For now, you should probably ignore it |
742
|
|
|
|
|
|
|
unless you read the code to understand how it works |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
=cut |
745
|
|
|
|
|
|
|
sub get { |
746
|
185900
|
|
|
185900
|
1
|
1970210
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
# Hash value, lock page, read result |
749
|
185900
|
|
|
|
|
472773
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
750
|
185900
|
|
|
|
|
397417
|
my $Unlock = $Self->_lock_page($HashPage); |
751
|
185899
|
|
|
|
|
661111
|
my ($Val, $Flags, $Found) = fc_read($Cache, $HashSlot, $_[1]); |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
# Value not found, check underlying data store |
754
|
185899
|
100
|
100
|
|
|
583093
|
if (!$Found && (my $read_cb = $Self->{read_cb})) { |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
# Callback to read from underlying data store |
757
|
|
|
|
|
|
|
# (unlock page first if we allow recursive calls |
758
|
21262
|
100
|
|
|
|
45417
|
$Unlock = undef if $Self->{allow_recursive}; |
759
|
21262
|
|
|
|
|
32466
|
$Val = eval { $read_cb->($Self->{context}, $_[1]); }; |
|
21262
|
|
|
|
|
44329
|
|
760
|
21262
|
|
|
|
|
79578
|
my $Err = $@; |
761
|
21262
|
100
|
|
|
|
44843
|
$Unlock = $Self->_lock_page($HashPage) if $Self->{allow_recursive}; |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
# Pass on any error |
764
|
21262
|
100
|
|
|
|
41971
|
if ($Err) { |
765
|
1
|
|
|
|
|
7
|
die $Err; |
766
|
|
|
|
|
|
|
} |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
# If we found it, or want to cache not-found, store back into our cache |
769
|
21261
|
100
|
100
|
|
|
60871
|
if (defined $Val || $Self->{cache_not_found}) { |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
# Are we doing writeback's? If so, need to mark as dirty in cache |
772
|
12923
|
|
|
|
|
19455
|
my $write_back = $Self->{write_back}; |
773
|
|
|
|
|
|
|
|
774
|
12923
|
100
|
|
|
|
25811
|
$Val = $Self->{serialize}(\$Val) if $Self->{serialize}; |
775
|
12923
|
50
|
|
|
|
24035
|
$Val = $Self->{compress}($Val) if $Self->{compress}; |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
# Get key/value len (we've got 'use bytes'), and do expunge check to |
778
|
|
|
|
|
|
|
# create space if needed |
779
|
12923
|
100
|
|
|
|
26644
|
my $KVLen = length($_[1]) + (defined($Val) ? length($Val) : 0); |
780
|
12923
|
|
|
|
|
29079
|
$Self->_expunge_page(2, 1, $KVLen); |
781
|
|
|
|
|
|
|
|
782
|
12923
|
|
|
|
|
36739
|
fc_write($Cache, $HashSlot, $_[1], $Val, -1, 0); |
783
|
|
|
|
|
|
|
} |
784
|
|
|
|
|
|
|
} |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
# Unlock page and return any found value |
787
|
|
|
|
|
|
|
# Unlock is done only if we're not in the middle of a get_set() operation. |
788
|
185898
|
|
66
|
|
|
452928
|
my $SkipUnlock = $_[2] && $_[2]->{skip_unlock}; |
789
|
185898
|
100
|
|
|
|
401525
|
$Unlock = undef unless $SkipUnlock; |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
# If not using raw values, use thaw() to turn data back into object |
792
|
185898
|
100
|
100
|
|
|
429097
|
$Val = $Self->{uncompress}($Val) if defined($Val) && $Self->{compress}; |
793
|
185898
|
100
|
100
|
|
|
674744
|
$Val = ${$Self->{deserialize}($Val)} if defined($Val) && $Self->{deserialize}; |
|
99309
|
|
|
|
|
239253
|
|
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
# If explicitly asked to skip unlocking, we return the reference to the unlocker |
796
|
185898
|
100
|
|
|
|
1765114
|
return ($Val, $Unlock) if $SkipUnlock; |
797
|
|
|
|
|
|
|
|
798
|
165889
|
|
|
|
|
358416
|
return $Val; |
799
|
|
|
|
|
|
|
} |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
=item I |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
Store specified key/value pair into cache |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
I<%Options> is optional, and is used by get_and_set() to control |
806
|
|
|
|
|
|
|
the locking behaviour. For now, you should probably ignore it |
807
|
|
|
|
|
|
|
unless you read the code to understand how it works |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
This method returns true if the value was stored in the cache, |
810
|
|
|
|
|
|
|
false otherwise. See the PAGE SIZE AND KEY/VALUE LIMITS section |
811
|
|
|
|
|
|
|
for more details. |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
=cut |
814
|
|
|
|
|
|
|
sub set { |
815
|
90968
|
|
|
90968
|
1
|
13840965
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
816
|
|
|
|
|
|
|
|
817
|
90968
|
100
|
|
|
|
214059
|
my $Val = $Self->{serialize} ? $Self->{serialize}(\$_[2]) : $_[2]; |
818
|
90968
|
100
|
|
|
|
315628
|
$Val = $Self->{compress}($Val) if $Self->{compress}; |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
# Get opts, make compatible with Cache::Cache interface |
821
|
90968
|
100
|
|
|
|
196641
|
my $Opts = defined($_[3]) ? (ref($_[3]) ? $_[3] : { expire_time => $_[3] }) : undef; |
|
|
100
|
|
|
|
|
|
822
|
90968
|
100
|
100
|
|
|
256768
|
my $expire_seconds = defined($Opts && $Opts->{expire_time}) ? parse_expire_time($Opts->{expire_time}) : -1; |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
# Hash value, lock page |
825
|
90968
|
|
|
|
|
245217
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
# If skip_lock is passed, it's a *reference* to an existing lock we |
828
|
|
|
|
|
|
|
# have to take and delete so we can cleanup below before calling |
829
|
|
|
|
|
|
|
# the callback |
830
|
90968
|
|
100
|
|
|
225763
|
my $Unlock = $Opts && $Opts->{skip_lock}; |
831
|
90968
|
100
|
|
|
|
172519
|
if ($Unlock) { |
832
|
15009
|
|
|
|
|
36310
|
($Unlock, $$Unlock) = ($$Unlock, undef); |
833
|
|
|
|
|
|
|
} else { |
834
|
75959
|
|
|
|
|
145994
|
$Unlock = $Self->_lock_page($HashPage); |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
# Are we doing writeback's? If so, need to mark as dirty in cache |
838
|
90968
|
|
|
|
|
169157
|
my $write_back = $Self->{write_back}; |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
# Get key/value len (we've got 'use bytes'), and do expunge check to |
841
|
|
|
|
|
|
|
# create space if needed |
842
|
90968
|
100
|
|
|
|
209318
|
my $KVLen = length($_[1]) + (defined($Val) ? length($Val) : 0); |
843
|
90968
|
|
|
|
|
227245
|
$Self->_expunge_page(2, 1, $KVLen); |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
# Now store into cache |
846
|
90968
|
100
|
|
|
|
343812
|
my $DidStore = fc_write($Cache, $HashSlot, $_[1], $Val, $expire_seconds, $write_back ? FC_ISDIRTY : 0); |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
# Unlock page |
849
|
90968
|
|
|
|
|
156125
|
$Unlock = undef; |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
# If we're doing write-through, or write-back and didn't get into cache, |
852
|
|
|
|
|
|
|
# write back to the underlying store |
853
|
90968
|
100
|
66
|
|
|
211213
|
if ((!$write_back || !$DidStore) && (my $write_cb = $Self->{write_cb})) { |
|
|
|
100
|
|
|
|
|
854
|
3000
|
|
|
|
|
5078
|
eval { $write_cb->($Self->{context}, $_[1], $_[2]); }; |
|
3000
|
|
|
|
|
7203
|
|
855
|
|
|
|
|
|
|
} |
856
|
|
|
|
|
|
|
|
857
|
90968
|
|
|
|
|
234529
|
return $DidStore; |
858
|
|
|
|
|
|
|
} |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
=item I |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
Atomically retrieve and set the value of a Key. |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
The page is locked while retrieving the $Key and is unlocked only after |
865
|
|
|
|
|
|
|
the value is set, thus guaranteeing the value does not change between |
866
|
|
|
|
|
|
|
the get and set operations. |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
$Sub is a reference to a subroutine that is called to calculate the |
869
|
|
|
|
|
|
|
new value to store. $Sub gets $Key and the current value |
870
|
|
|
|
|
|
|
as parameters, and |
871
|
|
|
|
|
|
|
should return the new value to set in the cache for the given $Key. |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
If the subroutine returns an empty list, no value is stored back |
874
|
|
|
|
|
|
|
in the cache. This avoids updating the expiry time on an entry |
875
|
|
|
|
|
|
|
if you want to do a "get if in cache, store if not present" type |
876
|
|
|
|
|
|
|
callback. |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
For example, to atomically increment a value in the cache, you |
879
|
|
|
|
|
|
|
can just use: |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
$Cache->get_and_set($Key, sub { return ++$_[1]; }); |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
In scalar context, the return value from this function is the *new* value |
884
|
|
|
|
|
|
|
stored back into the cache. |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
In list context, a two item array is returned; the new value stored |
887
|
|
|
|
|
|
|
back into the cache and a boolean that's true if the value was stored |
888
|
|
|
|
|
|
|
in the cache, false otherwise. See the PAGE SIZE AND KEY/VALUE LIMITS |
889
|
|
|
|
|
|
|
section for more details. |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
Notes: |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
=over 4 |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
=item * |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
Do not perform any get/set operations from the callback sub, as these |
898
|
|
|
|
|
|
|
operations lock the page and you may end up with a dead lock! |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=item * |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
If your sub does a die/throws an exception, the page will correctly |
903
|
|
|
|
|
|
|
be unlocked (1.15 onwards) |
904
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
=back |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
=cut |
908
|
|
|
|
|
|
|
sub get_and_set { |
909
|
15009
|
|
|
15009
|
1
|
135928
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
910
|
|
|
|
|
|
|
|
911
|
15009
|
|
|
|
|
54453
|
my ($Value, $Unlock) = $Self->get($_[1], { skip_unlock => 1 }); |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
# If this throws an error, $Unlock ref will still unlock page |
914
|
15009
|
|
|
|
|
66084
|
my @NewValue = $_[2]->($_[1], $Value); |
915
|
|
|
|
|
|
|
|
916
|
15009
|
|
|
|
|
84116
|
my $DidStore = 0; |
917
|
15009
|
50
|
|
|
|
44064
|
if (@NewValue) { |
918
|
15009
|
|
|
|
|
32979
|
($Value) = @NewValue; |
919
|
15009
|
|
|
|
|
63630
|
$DidStore = $Self->set($_[1], $Value, { skip_lock => \$Unlock }); |
920
|
|
|
|
|
|
|
} |
921
|
|
|
|
|
|
|
|
922
|
15009
|
100
|
|
|
|
85504
|
return wantarray ? ($Value, $DidStore) : $Value; |
923
|
|
|
|
|
|
|
} |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
=item I |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
Delete the given key from the cache |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
I<%Options> is optional, and is used by get_and_remove() to control |
930
|
|
|
|
|
|
|
the locking behaviour. For now, you should probably ignore it |
931
|
|
|
|
|
|
|
unless you read the code to understand how it works |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
=cut |
934
|
|
|
|
|
|
|
sub remove { |
935
|
11301
|
|
|
11301
|
1
|
52799
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
# Hash value, lock page, read result |
938
|
11301
|
|
|
|
|
33652
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
# If skip_lock is passed, it's a *reference* to an existing lock we |
941
|
|
|
|
|
|
|
# have to take and delete so we can cleanup below before calling |
942
|
|
|
|
|
|
|
# the callback |
943
|
11301
|
|
66
|
|
|
37352
|
my $Unlock = $_[2] && $_[2]->{skip_lock}; |
944
|
11301
|
100
|
|
|
|
26777
|
if ($Unlock) { |
945
|
5000
|
|
|
|
|
10406
|
($Unlock, $$Unlock) = ($$Unlock, undef); |
946
|
|
|
|
|
|
|
} else { |
947
|
6301
|
|
|
|
|
15677
|
$Unlock = $Self->_lock_page($HashPage); |
948
|
|
|
|
|
|
|
} |
949
|
|
|
|
|
|
|
|
950
|
11301
|
|
|
|
|
41331
|
my ($DidDel, $Flags) = fc_delete($Cache, $HashSlot, $_[1]); |
951
|
11301
|
|
|
|
|
22642
|
$Unlock = undef; |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
# If we deleted from the cache, and it's not dirty, also delete |
954
|
|
|
|
|
|
|
# from underlying store |
955
|
11301
|
100
|
66
|
|
|
30224
|
if ((!$DidDel || ($DidDel && !($Flags & FC_ISDIRTY))) |
|
|
|
66
|
|
|
|
|
956
|
|
|
|
|
|
|
&& (my $delete_cb = $Self->{delete_cb})) { |
957
|
301
|
|
|
|
|
487
|
eval { $delete_cb->($Self->{context}, $_[1]); }; |
|
301
|
|
|
|
|
587
|
|
958
|
|
|
|
|
|
|
} |
959
|
|
|
|
|
|
|
|
960
|
11301
|
|
|
|
|
31878
|
return $DidDel; |
961
|
|
|
|
|
|
|
} |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
=item I |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
Atomically retrieve value of a Key while removing it from the cache. |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
The page is locked while retrieving the $Key and is unlocked only after |
968
|
|
|
|
|
|
|
the value is removed, thus guaranteeing the value stored by someone else |
969
|
|
|
|
|
|
|
isn't removed by us. |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
=cut |
972
|
|
|
|
|
|
|
sub get_and_remove { |
973
|
5000
|
|
|
5000
|
1
|
21106
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
974
|
|
|
|
|
|
|
|
975
|
5000
|
|
|
|
|
15962
|
my ($Value, $Unlock) = $Self->get($_[1], { skip_unlock => 1 }); |
976
|
5000
|
|
|
|
|
19524
|
my $DidDel = $Self->remove($_[1], { skip_lock => \$Unlock }); |
977
|
5000
|
50
|
|
|
|
21151
|
return wantarray ? ($Value, $DidDel) : $Value; |
978
|
|
|
|
|
|
|
} |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
=item I |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
Clear all items from the cache |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
Note: If you're using callbacks, this has no effect |
985
|
|
|
|
|
|
|
on items in the underlying data store. No delete |
986
|
|
|
|
|
|
|
callbacks are made |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
=cut |
989
|
|
|
|
|
|
|
sub clear { |
990
|
4
|
|
|
4
|
1
|
4010
|
my $Self = shift; |
991
|
4
|
|
|
|
|
24
|
$Self->_expunge_all(1, 0); |
992
|
|
|
|
|
|
|
} |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
=item I |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
Clear all expired items from the cache |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
Note: If you're using callbacks, this has no effect |
999
|
|
|
|
|
|
|
on items in the underlying data store. No delete |
1000
|
|
|
|
|
|
|
callbacks are made, and no write callbacks are made |
1001
|
|
|
|
|
|
|
for the expired data |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
=cut |
1004
|
|
|
|
|
|
|
sub purge { |
1005
|
0
|
|
|
0
|
1
|
0
|
my $Self = shift; |
1006
|
0
|
|
|
|
|
0
|
$Self->_expunge_all(0, 0); |
1007
|
|
|
|
|
|
|
} |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
=item I |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
Empty all items from the cache, or if $OnlyExpired is |
1012
|
|
|
|
|
|
|
true, only expired items. |
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
Note: If 'write_back' mode is enabled, any changed items |
1015
|
|
|
|
|
|
|
are written back to the underlying store. Expired items are |
1016
|
|
|
|
|
|
|
written back to the underlying store as well. |
1017
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
=cut |
1019
|
|
|
|
|
|
|
sub empty { |
1020
|
5
|
|
|
5
|
1
|
2040
|
my $Self = shift; |
1021
|
5
|
50
|
|
|
|
53
|
$Self->_expunge_all($_[0] ? 0 : 1, 1); |
1022
|
|
|
|
|
|
|
} |
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
=item I |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
Get a list of keys/values held in the cache. May immediately be out of |
1027
|
|
|
|
|
|
|
date because of the shared access nature of the cache |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
If $Mode == 0, an array of keys is returned |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
If $Mode == 1, then an array of hashrefs, with 'key', |
1032
|
|
|
|
|
|
|
'last_access', 'expire_time' and 'flags' keys is returned |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
If $Mode == 2, then hashrefs also contain 'value' key |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
=cut |
1037
|
|
|
|
|
|
|
sub get_keys { |
1038
|
615
|
|
|
615
|
1
|
1842908
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1039
|
|
|
|
|
|
|
|
1040
|
615
|
|
100
|
|
|
3319
|
my $Mode = $_[1] || 0; |
1041
|
615
|
|
|
|
|
1932
|
my ($Uncompress, $Deserialize) = @$Self{qw(uncompress deserialize)}; |
1042
|
|
|
|
|
|
|
|
1043
|
615
|
100
|
33
|
|
|
4298076
|
return fc_get_keys($Cache, $Mode) |
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
1044
|
|
|
|
|
|
|
if $Mode <= 1 || ($Mode == 2 && !$Uncompress && !$Deserialize); |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
# If we're getting values as well, and they're not raw, unfreeze them |
1047
|
1
|
|
|
|
|
147
|
my @Details = fc_get_keys($Cache, 2); |
1048
|
|
|
|
|
|
|
|
1049
|
1
|
|
|
|
|
5
|
for (@Details) { |
1050
|
2
|
|
|
|
|
4
|
my $Val = $_->{value}; |
1051
|
2
|
50
|
|
|
|
6
|
if (defined $Val) { |
1052
|
2
|
50
|
|
|
|
5
|
$Val = $Uncompress->($Val) if $Uncompress; |
1053
|
2
|
50
|
|
|
|
5
|
$Val = ${$Deserialize->($Val)} if $Deserialize; |
|
2
|
|
|
|
|
6
|
|
1054
|
2
|
|
|
|
|
31
|
$_->{value} = $Val; |
1055
|
|
|
|
|
|
|
} |
1056
|
|
|
|
|
|
|
} |
1057
|
1
|
|
|
|
|
4
|
return @Details; |
1058
|
|
|
|
|
|
|
} |
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
=item I |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
Returns a two value list of (nreads, nreadhits). This |
1063
|
|
|
|
|
|
|
only works if you passed enable_stats in the constructor |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
nreads is the total number of read attempts done on the |
1066
|
|
|
|
|
|
|
cache since it was created |
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
nreadhits is the total number of read attempts done on |
1069
|
|
|
|
|
|
|
the cache since it was created that found the key/value |
1070
|
|
|
|
|
|
|
in the cache |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
If $Clear is true, the values are reset immediately after |
1073
|
|
|
|
|
|
|
they are retrieved |
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
=cut |
1076
|
|
|
|
|
|
|
sub get_statistics { |
1077
|
3
|
|
|
3
|
1
|
6499
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1078
|
3
|
|
|
|
|
13
|
my $Clear = $_[1]; |
1079
|
|
|
|
|
|
|
|
1080
|
3
|
|
|
|
|
12
|
my ($NReads, $NReadHits) = (0, 0); |
1081
|
3
|
|
|
|
|
22
|
for (0 .. $Self->{num_pages}-1) { |
1082
|
267
|
|
|
|
|
1062
|
my $Unlock = $Self->_lock_page($_); |
1083
|
267
|
|
|
|
|
1152
|
my ($PNReads, $PNReadHits) = fc_get_page_details($Cache); |
1084
|
267
|
|
|
|
|
662
|
$NReads += $PNReads; |
1085
|
267
|
|
|
|
|
523
|
$NReadHits += $PNReadHits; |
1086
|
267
|
100
|
|
|
|
1007
|
fc_reset_page_details($Cache) if $Clear; |
1087
|
267
|
|
|
|
|
1098
|
$Unlock = undef; |
1088
|
|
|
|
|
|
|
} |
1089
|
3
|
|
|
|
|
25
|
return ($NReads, $NReadHits); |
1090
|
|
|
|
|
|
|
} |
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
=item I |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
The two multi_xxx routines act a bit differently to the |
1095
|
|
|
|
|
|
|
other routines. With the multi_get, you pass a separate |
1096
|
|
|
|
|
|
|
PageKey value and then multiple keys. The PageKey value |
1097
|
|
|
|
|
|
|
is hashed, and that page locked. Then that page is |
1098
|
|
|
|
|
|
|
searched for each key. It returns a hash ref of |
1099
|
|
|
|
|
|
|
Key => Value items found in that page in the cache. |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
The main advantage of this is just a speed one, if you |
1102
|
|
|
|
|
|
|
happen to need to search for a lot of items on each call. |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
For instance, say you have users and a bunch of pieces |
1105
|
|
|
|
|
|
|
of separate information for each user. On a particular |
1106
|
|
|
|
|
|
|
run, you need to retrieve a sub-set of that information |
1107
|
|
|
|
|
|
|
for a user. You could do lots of get() calls, or you |
1108
|
|
|
|
|
|
|
could use the 'username' as the page key, and just |
1109
|
|
|
|
|
|
|
use one multi_get() and multi_set() call instead. |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
A couple of things to note: |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
=over 4 |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
=item 1. |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
This makes multi_get()/multi_set() and get()/set() |
1118
|
|
|
|
|
|
|
incompatible. Don't mix calls to the two, because |
1119
|
|
|
|
|
|
|
you won't find the data you're expecting |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
=item 2. |
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
The writeback and callback modes of operation do |
1124
|
|
|
|
|
|
|
not work with multi_get()/multi_set(). Don't attempt |
1125
|
|
|
|
|
|
|
to use them together. |
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
=back |
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
=cut |
1130
|
|
|
|
|
|
|
sub multi_get { |
1131
|
2
|
|
|
2
|
1
|
1012
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1132
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
# Hash value page key, lock page |
1134
|
2
|
|
|
|
|
6
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
1135
|
2
|
|
|
|
|
7
|
my $Unlock = $Self->_lock_page($HashPage); |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
# For each key to find |
1138
|
2
|
|
|
|
|
5
|
my ($Keys, %KVs) = ($_[2]); |
1139
|
2
|
|
|
|
|
5
|
for (@$Keys) { |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
# Hash key to get slot in this page and read |
1142
|
4
|
|
|
|
|
10
|
my $FinalKey = "$_[1]-$_"; |
1143
|
4
|
|
|
|
|
12
|
(undef, $HashSlot) = fc_hash($Cache, $FinalKey); |
1144
|
4
|
|
|
|
|
15
|
my ($Val, $Flags, $Found) = fc_read($Cache, $HashSlot, $FinalKey); |
1145
|
4
|
50
|
|
|
|
12
|
next unless $Found; |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
# If not using raw values, use thaw() to turn data back into object |
1148
|
4
|
50
|
33
|
|
|
21
|
$Val = $Self->{uncompress}($Val) if defined($Val) && $Self->{compress}; |
1149
|
4
|
50
|
33
|
|
|
19
|
$Val = ${$Self->{deserialize}($Val)} if defined($Val) && $Self->{deserialize}; |
|
0
|
|
|
|
|
0
|
|
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
# Save to return |
1152
|
4
|
|
|
|
|
12
|
$KVs{$_} = $Val; |
1153
|
|
|
|
|
|
|
} |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
# Unlock page and return any found value |
1156
|
2
|
|
|
|
|
4
|
$Unlock = undef; |
1157
|
|
|
|
|
|
|
|
1158
|
2
|
|
|
|
|
8
|
return \%KVs; |
1159
|
|
|
|
|
|
|
} |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
=item I $Value1, $Key2 => $Value2, ... }, [ \%Options ])> |
1162
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
Store specified key/value pair into cache |
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
=cut |
1166
|
|
|
|
|
|
|
sub multi_set { |
1167
|
2
|
|
|
2
|
1
|
2569
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1168
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
# Get opts, make compatible with Cache::Cache interface |
1170
|
2
|
0
|
|
|
|
8
|
my $Opts = defined($_[3]) ? (ref($_[3]) ? $_[3] : { expire_time => $_[3] }) : undef; |
|
|
50
|
|
|
|
|
|
1171
|
2
|
50
|
33
|
|
|
9
|
my $expire_seconds = defined($Opts && $Opts->{expire_time}) ? parse_expire_time($Opts->{expire_time}) : -1; |
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
# Hash page key value, lock page |
1174
|
2
|
|
|
|
|
7
|
my ($HashPage, $HashSlot) = fc_hash($Cache, $_[1]); |
1175
|
2
|
|
|
|
|
6
|
my $Unlock = $Self->_lock_page($HashPage); |
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
# Loop over each key/value storing into this page |
1178
|
2
|
|
|
|
|
3
|
my $KVs = $_[2]; |
1179
|
2
|
|
|
|
|
10
|
while (my ($Key, $Val) = each %$KVs) { |
1180
|
|
|
|
|
|
|
|
1181
|
4
|
50
|
|
|
|
11
|
$Val = $Self->{serialize}(\$Val) if $Self->{serialize}; |
1182
|
4
|
50
|
|
|
|
9
|
$Val = $Self->{compress}($Val) if $Self->{compress}; |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
# Get key/value len (we've got 'use bytes'), and do expunge check to |
1185
|
|
|
|
|
|
|
# create space if needed |
1186
|
4
|
|
|
|
|
7
|
my $FinalKey = "$_[1]-$Key"; |
1187
|
4
|
|
|
|
|
8
|
my $KVLen = length($FinalKey) + length($Val); |
1188
|
4
|
|
|
|
|
9
|
$Self->_expunge_page(2, 1, $KVLen); |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
# Now hash key and store into page |
1191
|
4
|
|
|
|
|
9
|
(undef, $HashSlot) = fc_hash($Cache, $FinalKey); |
1192
|
4
|
|
|
|
|
18
|
my $DidStore = fc_write($Cache, $HashSlot, $FinalKey, $Val, $expire_seconds, 0); |
1193
|
|
|
|
|
|
|
} |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
# Unlock page |
1196
|
2
|
|
|
|
|
5
|
$Unlock = undef; |
1197
|
|
|
|
|
|
|
|
1198
|
2
|
|
|
|
|
5
|
return 1; |
1199
|
|
|
|
|
|
|
} |
1200
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
=back |
1202
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
=cut |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
=head1 INTERNAL METHODS |
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
=over 4 |
1208
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
=cut |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
=item I<_expunge_all($Mode, $WB)> |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
Expunge all items from the cache |
1214
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
Expunged items (that have not expired) are written |
1216
|
|
|
|
|
|
|
back to the underlying store if write_back is enabled |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
=cut |
1219
|
|
|
|
|
|
|
sub _expunge_all { |
1220
|
9
|
|
|
9
|
|
49
|
my ($Self, $Cache, $Mode, $WB) = ($_[0], $_[0]->{Cache}, $_[1], $_[2]); |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
# Repeat expunge for each page |
1223
|
9
|
|
|
|
|
57
|
for (0 .. $Self->{num_pages}-1) { |
1224
|
557
|
|
|
|
|
1362
|
my $Unlock = $Self->_lock_page($_); |
1225
|
557
|
|
|
|
|
1691
|
$Self->_expunge_page($Mode, $WB, -1); |
1226
|
557
|
|
|
|
|
1918
|
$Unlock = undef; |
1227
|
|
|
|
|
|
|
} |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
} |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
=item I<_expunge_page($Mode, $WB, $Len)> |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
Expunge items from the current page to make space for |
1234
|
|
|
|
|
|
|
$Len bytes key/value items |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
Expunged items (that have not expired) are written |
1237
|
|
|
|
|
|
|
back to the underlying store if write_back is enabled |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
=cut |
1240
|
|
|
|
|
|
|
sub _expunge_page { |
1241
|
104452
|
|
|
104452
|
|
257042
|
my ($Self, $Cache, $Mode, $WB, $Len) = ($_[0], $_[0]->{Cache}, @_[1 .. 3]); |
1242
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
# If writeback mode, need to get expunged items to write back |
1244
|
104452
|
100
|
100
|
|
|
358082
|
my $write_cb = $Self->{write_back} && $WB ? $Self->{write_cb} : undef; |
1245
|
|
|
|
|
|
|
|
1246
|
104452
|
100
|
|
|
|
420816
|
my @WBItems = fc_expunge($Cache, $Mode, $write_cb ? 1 : 0, $Len); |
1247
|
|
|
|
|
|
|
|
1248
|
104452
|
|
|
|
|
208223
|
my ($Uncompress, $Deserialize) = @$Self{qw(uncompress deserialize)}; |
1249
|
|
|
|
|
|
|
|
1250
|
104452
|
|
|
|
|
240517
|
for (@WBItems) { |
1251
|
40643
|
100
|
|
|
|
133351
|
next if !($_->{flags} & FC_ISDIRTY); |
1252
|
|
|
|
|
|
|
|
1253
|
32687
|
|
|
|
|
50858
|
my $Val = $_->{value}; |
1254
|
32687
|
100
|
|
|
|
64778
|
if (defined $Val) { |
1255
|
29429
|
50
|
|
|
|
56840
|
$Val = $Uncompress->($Val) if $Uncompress; |
1256
|
29429
|
100
|
|
|
|
56158
|
$Val = ${$Deserialize->($Val)} if $Deserialize; |
|
2
|
|
|
|
|
7
|
|
1257
|
|
|
|
|
|
|
} |
1258
|
32687
|
|
|
|
|
46475
|
eval { $write_cb->($Self->{context}, $_->{key}, $Val, $_->{expire_time}); }; |
|
32687
|
|
|
|
|
66485
|
|
1259
|
|
|
|
|
|
|
} |
1260
|
|
|
|
|
|
|
} |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
=item I<_lock_page($Page)> |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
Lock a given page in the cache, and return an object |
1265
|
|
|
|
|
|
|
reference that when DESTROYed, unlocks the page |
1266
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
=cut |
1268
|
|
|
|
|
|
|
sub _lock_page { |
1269
|
268989
|
|
|
268989
|
|
491336
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1270
|
|
|
|
|
|
|
my $Unlock = Cache::FastMmap::OnLeave->new(sub { |
1271
|
268989
|
100
|
|
268989
|
|
1703191
|
fc_unlock($Cache) if fc_is_locked($Cache); |
1272
|
268989
|
|
|
|
|
970392
|
}); |
1273
|
268989
|
|
|
|
|
3365773
|
fc_lock($Cache, $_[1]); |
1274
|
268988
|
|
|
|
|
549396
|
return $Unlock; |
1275
|
|
|
|
|
|
|
} |
1276
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
sub parse_expire_time { |
1278
|
8044
|
|
100
|
8044
|
0
|
35100
|
my $expire_time = shift || ''; |
1279
|
8044
|
100
|
|
|
|
21601
|
return 1 if $expire_time eq 'now'; |
1280
|
8042
|
100
|
|
|
|
19013
|
return 0 if $expire_time eq 'never'; |
1281
|
8040
|
|
|
|
|
26165
|
my %Times = ('' => 1, s => 1, m => 60, h => 60*60, d => 24*60*60, w => 7*24*60*60); |
1282
|
8040
|
100
|
|
|
|
32670
|
return $expire_time =~ /^(\d+)\s*([mhdws]?)/i ? $1 * $Times{lc($2)} : 0; |
1283
|
|
|
|
|
|
|
} |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
sub cleanup { |
1286
|
8024
|
|
|
8024
|
0
|
23914
|
my ($Self, $Cache) = ($_[0], $_[0]->{Cache}); |
1287
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
# Avoid potential double cleanup |
1289
|
8024
|
50
|
|
|
|
19909
|
return if $Self->{cleaned}; |
1290
|
8024
|
|
|
|
|
15375
|
$Self->{cleaned} = 1; |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
# Expunge all entries on exit if requested and in parent process |
1293
|
8024
|
50
|
66
|
|
|
21712
|
if ($Self->{empty_on_exit} && $Cache && $Self->{pid} == $$) { |
|
|
|
66
|
|
|
|
|
1294
|
1
|
|
|
|
|
5
|
$Self->empty(); |
1295
|
|
|
|
|
|
|
} |
1296
|
|
|
|
|
|
|
|
1297
|
8024
|
50
|
|
|
|
19758
|
if ($Cache) { |
1298
|
8024
|
|
|
|
|
71225
|
fc_close($Cache); |
1299
|
8024
|
|
|
|
|
15257
|
$Cache = undef; |
1300
|
8024
|
|
|
|
|
17755
|
delete $Self->{Cache}; |
1301
|
|
|
|
|
|
|
} |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
unlink($Self->{share_file}) |
1304
|
8024
|
100
|
66
|
|
|
936258
|
if $Self->{unlink_on_exit} && $Self->{pid} == $$; |
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
} |
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
sub DESTROY { |
1309
|
8024
|
|
|
8024
|
|
84010
|
my $Self = shift; |
1310
|
8024
|
|
|
|
|
23223
|
$Self->cleanup(); |
1311
|
8024
|
100
|
|
|
|
92247
|
delete $LiveCaches{ref($Self)} if $Self->{empty_on_exit}; |
1312
|
|
|
|
|
|
|
} |
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
sub END { |
1315
|
18
|
|
|
18
|
|
43533
|
while (my (undef, $Self) = each %LiveCaches) { |
1316
|
|
|
|
|
|
|
# Weak reference, might be undef already |
1317
|
0
|
0
|
|
|
|
0
|
$Self->cleanup() if $Self; |
1318
|
|
|
|
|
|
|
} |
1319
|
18
|
|
|
|
|
175
|
%LiveCaches = (); |
1320
|
|
|
|
|
|
|
} |
1321
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
sub CLONE { |
1323
|
0
|
|
|
0
|
|
0
|
die "Cache::FastMmap does not support threads sorry"; |
1324
|
|
|
|
|
|
|
} |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
1; |
1327
|
|
|
|
|
|
|
|
1328
|
|
|
|
|
|
|
package Cache::FastMmap::OnLeave; |
1329
|
18
|
|
|
18
|
|
186
|
use strict; |
|
18
|
|
|
|
|
52
|
|
|
18
|
|
|
|
|
2312
|
|
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
sub new { |
1332
|
268989
|
|
|
268989
|
|
482987
|
my $Class = shift; |
1333
|
268989
|
|
|
|
|
419668
|
my $Ref = \$_[0]; |
1334
|
268989
|
|
|
|
|
495265
|
bless $Ref, $Class; |
1335
|
268989
|
|
|
|
|
495119
|
return $Ref; |
1336
|
|
|
|
|
|
|
} |
1337
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
sub disable { |
1339
|
0
|
|
|
0
|
|
0
|
${$_[0]} = undef; |
|
0
|
|
|
|
|
0
|
|
1340
|
|
|
|
|
|
|
} |
1341
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
sub DESTROY { |
1343
|
268989
|
|
|
268989
|
|
451874
|
my $e = $@; # Save errors from code calling us |
1344
|
268989
|
|
|
|
|
412365
|
eval { |
1345
|
|
|
|
|
|
|
|
1346
|
268989
|
|
|
|
|
405962
|
my $Ref = shift; |
1347
|
268989
|
50
|
|
|
|
717102
|
$$Ref->() if $$Ref; |
1348
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
}; |
1350
|
|
|
|
|
|
|
# $e .= " (in cleanup) $@" if $@; |
1351
|
268989
|
|
|
|
|
1663066
|
$@ = $e; |
1352
|
|
|
|
|
|
|
} |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
1; |
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
__END__ |