File Coverage

blib/lib/Tie/ShareLite.pm
Criterion Covered Total %
statement 99 135 73.3
branch 33 76 43.4
condition 16 33 48.4
subroutine 17 23 73.9
pod 4 4 100.0
total 169 271 62.3


line stmt bran cond sub pod time code
1             # Tie::ShareLite
2             #
3             # class to tie a hash to IPC::ShareLite and automatically update using Storable
4             #
5             # This module is free software; you may redistribute it and/or
6             # modify it under the same terms as Perl itself.
7              
8             =head1 NAME
9            
10             Tie::ShareLite - Tied hash interface to IPC::ShareLite
11            
12             =head1 SYNOPSIS
13            
14             use Tie::ShareLite qw( :lock );
15              
16             $ipc = tie %shared, 'Tie::ShareLite', -key => 1971,
17             -mode => 0600,
18             -create => 'yes',
19             -destroy => 'no'
20             or die("Could not tie to shared memory: $!");
21              
22             $shared{'myKey'} = "This is stored in shared memory");
23              
24             $ipc->lock( LOCK_EX );
25             $shared{'var1'} = 'some value';
26             $shared{'var2'} = 'some other value';
27             $ipc->unlock();
28            
29             =head1 DESCRIPTION
30              
31             Tie::ShareLite provides for a tied hash interface to the IPC::ShareLite module
32             that is very similar to the one provided by IPC::Shareable. Only hashes can be
33             tied at this time. The hashes can be of any complexity allowed by the Storable
34             module, however, there are some caveats covered below in the REFERENCES section.
35              
36             To tie a hash to shared memory, use the tie command:
37              
38             $ipc = tie %shared, 'Tie::ShareLite', -key => 1971,
39             -mode => 0600,
40             -create => 'yes',
41             -destroy => 'no'
42             or die("Could not tie to shared memory: $!");
43            
44             Any parameters you pass (such as -key, -mode, -create, etc) are passed straight
45             through to IPC::ShareLite. After this call, the contents of the hash %shared
46             are now in shared memory, and the $ipc variable can be used to lock the memory
47             segment.
48              
49             To update the shared memory, simply assign something to it like you would any
50             other hash:
51            
52             $shared{'myKey'} = "This is stored in shared memory");
53              
54             Each read and write to the hash is atomic. In the background IPC::ShareLite
55             makes sure that each process takes their turn.
56              
57             You can make several operations atomic by calling lock() and unlock():
58            
59             $ipc->lock( LOCK_EX );
60             $shared{'var1'} = 'some value';
61             $shared{'var2'} = 'some other value';
62             $ipc->unlock();
63              
64             I suggest locking any time you do multiple reads or writes. If you have a
65             read or write operation in a loop, locking before the loop can speed things up
66             a lot. This is because when a lock is in place, the module thaws the data from
67             shared memory once and keeps it in memory until you unlock. The following code
68             illustrates how this works. The comments show you what happens behind the
69             scenes. fetch() means that the data is read from shared memory and thawed.
70             store() means that the data is frozen, then written to shared memory. Both
71             the fetch and store operations are relatively expensive, so reducing how many
72             times they happen can speed up your code a lot.
73              
74             $shared{'name'} = 'Fred'; # fetch(), update, store()
75             $shared{'title'} = 'Manager'; # fetch(), update, store()
76              
77             $ipc->lock( LOCK_EX );
78             $shared{'age'} = '45'; # fetch(), update
79             $shared{'sex'} = 'male'; # update
80             $shared{'dept'} = 'sales'; # update
81             $ipc->unlock(); # store()
82              
83             print "Name: " . $shared{'name'} . "\n"; # fetch()
84             print "Title: " . $shared{'title'} . "\n"; # fetch()
85              
86             $ipc->lock( LOCK_SH );
87             print "Age: " . $shared{'age'} . "\n"; # fetch()
88             print "Sex: " . $shared{'sex'} . "\n";
89             print "Dept: " . $shared{'dept'} . "\n";
90             $ipc->unlock();
91              
92             Tie::ShareLite will keep tabs on locks and smartly fetch and store the data
93             only when needed.
94              
95             =head1 METHODS
96              
97             =over 4
98              
99             =item lock( $mode )
100              
101             Obtains a lock on the shared memory by calling IPC::ShareLite::lock().
102              
103             =item unlock()
104              
105             Releases a lock on the shared memory by calling IPC::ShareLite::unlock().
106              
107             =item shlock( $mode )
108              
109             Calls lock(). Here for drop-in compatibility with IPC::Shareable.
110              
111             =item shunlock()
112              
113             Calls unlock(). Here for drop-in compatibility with IPC::Shareable.
114              
115             =head1 REFERENCES
116              
117             Storing references in tied hashes is not very well supported by Perl. There
118             are a few gotchas that are not very obvious. When you say something like
119              
120             $shared{'key'}{'subkey'} = 'value';
121              
122             Perl actually creates a real anonymous hash with nothing in it,
123             assigns the reference of that hash to $shared{'key'}, then finally puts the
124             subkey => value part into the anonymous hash. This anonymous hash only exist
125             in the current process, and after the whole shared hash is serialized, that
126             reference is lost. Plus the tied hash is never told about the change to the
127             anonymouse hash. So in other words, it doesn't work.
128              
129             IPC::Shareable "solved" this problem by tying the anonymous hash as another
130             shared hash. This has the downside of using up a lot of shared memory segments
131             very fast. Plus it has some weird side effects that have caused me problems
132             in the past. In this module, for now I have decided to forgo any kind of
133             special hacks to get this to work. So if you want to share complex hashes
134             then you have to copy the hash into local memory, access it as you want, then
135             assign it back to the shared hash. Example:
136              
137             $ipc->lock();
138             my $tmp = $shared{'key'};
139             $tmp->{'subkey'} = 'value';
140             $shared{'key'} = $tmp;
141             $ipc->unlock();
142              
143             I would suggest putting the lock there, otherwise another process could change
144             the contents of the 'key' between when you read it, and when you write it back
145             and thus your process would overwrite the others change.
146              
147             Luckily, reads don't have this problem and are much simpler:
148              
149             my $value = $shared{'key'}['subkey'};
150              
151             I have played around with a possible solution to this, but I have a feeling
152             it would add some serious overhead that would slow the whole module down. I
153             would be more than happy to hear from anyone that has found a clean solution
154             to this.
155              
156             =head1 EXPORT
157              
158             Anything that IPC::ShareLite exports.
159              
160             =head1 AUTHOR
161              
162             Copyright 2004, Nathan Shafer Enate-tiesharelite@seekio.comE.
163             All rights reserved.
164              
165             This module is free software; you may redistribute it and/or
166             modify it under the same terms as Perl itself.
167              
168             =head1 CREDITS
169              
170             Special thanks to Maurice Aubrey for his wonderful module, IPC::ShareLite.
171              
172             =head1 SEE ALSO
173              
174             L, L.
175              
176             =cut
177              
178             package Tie::ShareLite;
179 1     1   75114 use 5.006;
  1         4  
  1         40  
180 1     1   5 use strict;
  1         2  
  1         28  
181 1     1   794 use IPC::ShareLite qw(:all);
  1         113519  
  1         347  
182 1     1   1769 use Storable qw(freeze thaw);
  1         5339  
  1         117  
183 1     1   9 use Carp;
  1         3  
  1         90  
184              
185 1     1   8 use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG );
  1         18  
  1         2787  
186              
187             # Pass all the exports from IPC::ShareLite through. We have none of our own.
188             require Exporter;
189             @ISA = qw(Exporter);
190             @EXPORT = @IPC::ShareLite::EXPORT;
191             @EXPORT_OK = @IPC::ShareLite::EXPORT_OK;
192             %EXPORT_TAGS = %IPC::ShareLite::EXPORT_TAGS;
193              
194             $VERSION = '0.03';
195             $DEBUG = 0;
196              
197             sub TIEHASH {
198 1 50   1   132 print STDERR "TIEHASH(@_)\n" if $DEBUG;
199 1         5 my($class, @params) = @_;
200              
201 1         2 my $this = {};
202 1         3 bless($this, $class);
203              
204 1         9 $this->{_lock} = undef;
205 1         3 $this->{_lock_return} = undef;
206 1         3 $this->{_internal_lock} = undef;
207 1         3 $this->{_iterating} = 0;
208              
209 1 50       12 $this->{share} = new IPC::ShareLite(@params)
210             or croak("Could not create new IPC::ShareLite object: $!");
211              
212 1         349 return($this);
213             }
214              
215             sub FETCH {
216 4 50   4   238 print STDERR "FETCH(@_)\n" if $DEBUG;
217 4         5 my($this, $key) = @_;
218              
219 4         8 $this->_get_hash();
220 4         6 $this->{_iterating} = 0;
221 4         501 return($this->{_hash}->{$key});
222             }
223              
224             sub STORE {
225 3 50   3   2246 print STDERR "STORE(@_)\n" if $DEBUG;
226 3         182 my($this, $key, $value) = @_;
227              
228 3         797 $this->_smart_lock( LOCK_EX );
229 3         10 $this->_get_hash();
230 3         8 $this->{_hash}->{$key} = $value;
231 3         9 $this->_put_hash();
232 3         31 $this->_smart_unlock();
233             }
234              
235             sub DELETE {
236 1 50   1   44 print STDERR "DELETE(@_)\n" if $DEBUG;
237 1         2 my($this, $key) = @_;
238              
239 1         23 $this->_smart_lock( LOCK_EX );
240 1         3 $this->_get_hash();
241 1         3 delete($this->{_hash}->{$key});
242 1         3 $this->_put_hash();
243 1         3 $this->_smart_unlock();
244             }
245              
246             sub CLEAR {
247 0 0   0   0 print STDERR "CLEAR(@_)\n" if $DEBUG;
248 0         0 my($this) = @_;
249              
250 0         0 $this->{_hash} = {};
251 0         0 $this->_put_hash();
252             }
253              
254             sub EXISTS {
255 2 50   2   54 print STDERR "EXISTS(@_)\n" if $DEBUG;
256 2         3 my($this, $key) = @_;
257              
258 2         5 $this->_get_hash();
259 2         8 return(exists($this->{_hash}->{$key}));
260             }
261              
262             sub FIRSTKEY {
263 0 0   0   0 print STDERR "FIRSTKEY(@_)\n" if $DEBUG;
264 0         0 my($this) = @_;
265              
266 0         0 $this->_get_hash();
267 0         0 my $reset = keys(%{$this->{_hash}});
  0         0  
268 0         0 my $first = each(%{$this->{_hash}});
  0         0  
269 0         0 $this->{_iterating} = 1;
270 0         0 return($first);
271             }
272              
273             sub NEXTKEY {
274 0 0   0   0 print STDERR "NEXTKEY(@_)\n" if $DEBUG;
275 0         0 my($this, $lastkey) = @_;
276              
277 0         0 my $next = each(%{$this->{_hash}});
  0         0  
278 0 0       0 if($next) {
279 0         0 $this->{_iterating} = 1;
280             }
281 0         0 return($next);
282             }
283              
284             sub DESTROY {
285 0 0   0   0 print STDERR "DESTROY(@_)\n" if $DEBUG;
286 0         0 my($this) = @_;
287              
288 0         0 $this->unlock();
289             }
290              
291             sub lock {
292 4 50   4 1 66 print STDERR "lock(@_)\n" if $DEBUG;
293 4         5 my($this, $flags) = @_;
294              
295 4   33     11 $flags ||= LOCK_EX;
296              
297 4         14 my $return = $this->{share}->lock($flags);
298 4         83 $this->{_lock} = $flags;
299 4         6 $this->{_lock_return} = $return;
300              
301 4         7 undef($this->{_hash});
302              
303 4         11 return($return);
304             }
305              
306             sub unlock {
307 4 50   4 1 45 print STDERR "unlock(@_)\n" if $DEBUG;
308 4         5 my($this) = @_;
309              
310             # flush any unsaved changes
311 4         10 $this->_put_hash(1);
312              
313 4         15 my $return = $this->{share}->unlock();
314 4         41 $this->{_lock} = undef;
315 4         5 $this->{_lock_return} = $return;
316 4         6 $this->{_internal_lock} = 0;
317 4         5 undef($this->{_hash});
318              
319 4         21 return($return);
320             }
321              
322             sub shlock {
323 0 0   0 1 0 print STDERR "shlock(@_)\n" if $DEBUG;
324 0         0 my($this, @params) = @_;
325              
326 0         0 return($this->lock(@params));
327             }
328              
329             sub shunlock {
330 0 0   0 1 0 print STDERR "shunlock(@_)\n" if $DEBUG;
331 0         0 my($this) = @_;
332              
333 0         0 return($this->unlock());
334             }
335              
336             sub _get_hash {
337 10 50   10   210 print STDERR "_get_hash(@_)\n" if $DEBUG;
338 10         15 my($this) = @_;
339              
340 10 100 33     35 unless(defined($this->{_hash}) && ($this->{_lock} || $this->{_iterating})) {
      66        
341 8 50       12 print STDERR "_get_hash: thawing data\n" if $DEBUG;
342 8         27 my $serialized = $this->{share}->fetch();
343              
344 8 50       94 if($serialized) {
345 8         22 $this->{_hash} = thaw($serialized);
346             } else {
347 0         0 $this->{_hash} = {};
348             }
349             }
350              
351 10         170 return();
352             }
353              
354             sub _put_hash {
355 8 50   8   16 print STDERR "_put_hash(@_)\n" if $DEBUG;
356 8         11 my($this, $flush) = @_;
357              
358 8 100 66     142 if(!$flush && ($this->{_lock} == LOCK_EX ||
    100 66        
      66        
      66        
359             ($this->{_lock} == (LOCK_EX|LOCK_NB) && $this->{_lock_return})))
360             {
361 3 50       19 print STDERR "_put_hash: setting _need_flush!\n" if $DEBUG;
362 3         8 $this->{_need_flush} = 1;
363             } elsif(!$flush || ($flush && $this->{_need_flush})) {
364 4 50       70 print STDERR "_put_hash: flushing!\n" if $DEBUG;
365 4         14 my $serialized = freeze($this->{_hash});
366 4         514 $this->{share}->store($serialized);
367 4         35 $this->{_need_flush} = 0;
368             } else {
369 1 50       3 print STDERR "_put_hash: doing nothing!\n" if $DEBUG;
370             }
371             }
372              
373             sub _smart_lock {
374 4 50   4   111 print STDERR "_smart_lock(@_)\n" if $DEBUG;
375 4         8 my($this, $flags) = @_;
376              
377 4 50       967 if($flags == LOCK_SH) {
    50          
378             # we only have to check if a lock has been set successfully, we don't care
379             # which
380 0 0 0     0 unless($this->{_lock} && $this->{_lock_return}) {
381 0 0       0 print STDERR "_smart_lock: setting shared lock\n" if $DEBUG;
382 0         0 $this->{_internal_lock} = 1;
383 0         0 return($this->lock($flags));
384             }
385             } elsif($flags == LOCK_EX) {
386             # if the lock is LOCK_SH or LOCK_SH|LOCK_NB then we escalate it to a
387             # LOCK_EX temporarily. Otherwise we just set a LOCK_EX
388 4 50 33     780 if($this->{_lock} == LOCK_SH || ($this->{_lock} == (LOCK_SH|LOCK_NB) &&
    100 33        
      66        
389             $this->{_lock_return}))
390             {
391 0 0       0 print STDERR "_smart_lock: escalating lock to exclusive\n" if $DEBUG;
392 0         0 return($this->lock($flags));
393             } elsif(!$this->{_lock} || !$this->{_lock_return}) {
394 3 50       224 print STDERR "_smart_lock: setting exclusive lock\n" if $DEBUG;
395 3         6 $this->{_internal_lock} = 1;
396 3         10 return($this->lock($flags));
397             } else {
398 1 50       1457 print STDERR "_smart_lock: lock already set\n" if $DEBUG;
399             }
400             }
401             }
402              
403             sub _smart_unlock {
404 4 50   4   10 print STDERR "_smart_unlock(@_)\n" if $DEBUG;
405 4         6 my($this) = @_;
406              
407             # Only unlock if it was us that originally locked
408 4 100       14 if($this->{_internal_lock}) {
409 3 50       7 print STDERR "_smart_unlock: unlocking\n" if $DEBUG;
410 3         8 $this->unlock();
411             }
412             }
413              
414             1;