File Coverage

blib/lib/Apache/SharedMem.pm
Criterion Covered Total %
statement 78 476 16.3
branch 12 256 4.6
condition 7 82 8.5
subroutine 20 55 36.3
pod 16 18 88.8
total 133 887 14.9


line stmt bran cond sub pod time code
1             package Apache::SharedMem;
2             #$Id: SharedMem.pm,v 1.61 2001/10/04 12:15:22 rs Exp $
3              
4             =pod
5              
6             =head1 NAME
7              
8             Apache::SharedMem - Share data between Apache children processes through the shared memory
9              
10             =head1 SYNOPSIS
11              
12             use Apache::SharedMem qw(:lock :status);
13              
14             my $share = new Apache::SharedMem || die($Apache::SharedMem::ERROR);
15              
16             $share->set(key => 'some data');
17              
18             # ...maybe in another apache child
19             my $var = $share->get(key);
20              
21             $share->delete(key);
22              
23             # delete all keys if the total size is larger than $max_size
24             $share->clear if($share->size > $max_size);
25              
26             # using an exclusive blocking lock, but with a timeout
27             my $lock_timeout = 40; # seconds
28             if($share->lock(LOCK_EX, $lock_timeout))
29             {
30             my $data =...
31             ...some traitement...
32            
33             $share->set(key => $data); # the implicite lock is not overrided
34             warn('failed to store data in shared memory') if($share->status & FAILURE);
35              
36             $share->unlock;
37             }
38            
39             $share->release;
40              
41             =head1 DESCRIPTION
42              
43             This module make it easier to share data between Apache children processes through shared memory.
44             This module internal functionment is much inspired from IPC::SharedCache, but without any cache management.
45             The share memory segment key is automatically deduced by the caller package, which means that 2 modules
46             can use same keys without being concerned about namespace clash. An additionnal namespace is used per application,
47             which means that the same module, with the same namespace used in two applications doesn't clash too. Application
48             distinction is made on two things : the process' UID and DOCUMENT_ROOT (for http applications) or current
49             working directory.
50              
51             This module handles all shared memory interaction via the IPC::SharedLite and all data
52             serialization with Storable. See L and L for details.
53              
54             =head1 USAGE
55              
56             If you are running under mod_perl, you should put this line in your httpd.conf:
57              
58             # must be a valid path
59             PerlAddVar PROJECT_DOCUMENT_ROOT /path/to/your/projects/root
60              
61             and in your startup.pl:
62              
63             use Apache::SharedMem;
64              
65             This allow Apache::SharedMem to determine a unique rootkey for all virtual hosts,
66             and to cleanup this rootkey on Apache stop. PROJECT_DOCUMENT_ROOT is used instead of a
67             per virtal host's DOCUMENT_ROOT for rootkey's generation.
68              
69             You can also provide a PROJECT_ID, it's the server's uid by default. This value have to
70             be numeric:
71              
72             PerlAddVar PROJECT_ID 10
73              
74             =cut
75              
76             BEGIN
77             {
78 5     5   35091 use strict;
  5         10  
  5         172  
79 5     5   98 use 5.005;
  5         14  
  5         153  
80 5     5   24 use Carp;
  5         12  
  5         313  
81 5     5   4697 use IPC::SysV qw();
  5         6602  
  5         153  
82 5     5   4613 use IPC::ShareLite qw(:lock);
  5         30668  
  5         909  
83 5     5   5907 use Storable qw(freeze thaw);
  5         19093  
  5         420  
84              
85 5     5   42 use base qw(Exporter);
  5         8  
  5         966  
86              
87 5     5   64 %Apache::SharedMem::EXPORT_TAGS =
88             (
89             'all' => [qw(
90             LOCK_EX LOCK_SH LOCK_UN LOCK_NB
91             WAIT NOWAIT
92             SUCCESS FAILURE
93             )],
94             'lock' => [qw(LOCK_EX LOCK_SH LOCK_UN LOCK_NB)],
95             'wait' => [qw(WAIT NOWAIT)],
96             'status'=> [qw(SUCCESS FAILURE)],
97             );
98 5         39 @Apache::SharedMem::EXPORT_OK = @{$Apache::SharedMem::EXPORT_TAGS{'all'}};
  5         24  
99              
100 5     5   31 use constant WAIT => 1;
  5         12  
  5         399  
101 5     5   26 use constant NOWAIT => 0;
  5         10  
  5         214  
102 5     5   87 use constant SUCCESS => 1;
  5         11  
  5         199  
103 5     5   23 use constant FAILURE => 2;
  5         15  
  5         211  
104              
105             # default values
106 5     5   24 use constant IPC_MODE => 0600;
  5         8  
  5         182  
107 5     5   27 use constant IPC_SEGSIZE=> 65_536;
  5         8  
  5         242  
108              
109 5         36383 $Apache::SharedMem::VERSION = '0.09';
110             }
111              
112             # main
113             {
114             if(exists $ENV{'GATEWAY_INTERFACE'} && $ENV{'GATEWAY_INTERFACE'} =~ /^CGI-Perl/
115             && defined $Apache::Server::Starting && $Apache::Server::Starting)
116             {
117             # we are under startup.pl
118             if($Apache::SharedMem::ROOTKEY = _get_rootkey())
119             {
120             Apache->server->register_cleanup(\&Apache::SharedMem::_cleanup);
121             }
122             else
123             {
124             print(STDERR "Apache::SharedMem: can't determine the global root key, have you put 'PerlAddVar PROJECT_DOCUMENT_ROOT /path/to/your/project/root/' in your httpd.conf ?\n");
125             }
126             }
127             }
128              
129             =pod
130              
131             =head1 METHODS
132              
133             =head2 new (namespace => 'Namespace', ipc_mode => 0666, ipc_segment_size => 1_000, debug => 1)
134              
135             =over 4
136              
137             =item *
138              
139             C optional, integer
140              
141             Changes the root segment key. It must be an unsigned integer. Don't use this
142             option unless you really know what you are doing.
143              
144             This key allows Apache::SharedMem to find the root map of all namespaces (see below)
145             owned by your application.
146              
147             The rootkey is automatically generated using the C provided by IPC::SysV.
148             Process' UID and DOCUMENT_ROOT (or current working directory) are given to C
149             so as to guarantee an unique key as far as possible.
150              
151             Note, if you are using mod_perl, and you'v load mod_perl via startup.pl
152             (see USAGE section for more details), the rootkey is generated once at the apache
153             start, based on the supplied PROJECT_DOCUMENT_ROOT and Apache's uid.
154              
155             =item *
156              
157             C optional, string
158              
159             Setup manually the namespace. To share same datas, your program must use the same
160             namespace. This namespace is set by default to the caller's package name. In most
161             cases the default value is a good choice. But you may setup manually this value if,
162             for example, you want to share the same datas between two or more modules.
163              
164             =item *
165              
166             C optional, octal
167              
168             Setup manually the segment mode (see L) for more details (default: 0600).
169             Warning: this value _must_ be octal, see chmod documentation in perlfunc manpage for more details.
170              
171             =item *
172              
173             C optional, integer
174              
175             Setup manually the segment size (see L) for more details (default: 65_536).
176              
177             =item *
178              
179             C optional, boolean
180              
181             Turn on/off the debug mode (default: 0)
182              
183             =back
184              
185             In most case, you don't need to give any arguments to the constructor.
186              
187             C and C are used only on the first namespace
188             initialisation. Using different values on an existing key (in shared memory)
189             has no effect.
190              
191             Note that C is default value of IPC::ShareLite, see
192             L
193              
194             On succes return an Apache::SharedMem object, on error, return undef().
195             You can get error string via $Apache::SharedMem::ERROR.
196              
197             =cut
198              
199             sub new
200             {
201 4     4 1 1100 my $pkg = shift;
202 4   33     49 my $self = bless({}, ref($pkg) || $pkg);
203              
204 4         70 my $options = $self->{options} =
205             {
206             rootname => undef, # obsolete, use rootkey instead
207             rootkey => undef, # if not spécified, take the rootname value if exists or _get_rootkey()
208             namespace => (caller())[0],
209             ipc_mode => IPC_MODE,
210             ipc_segment_size => IPC_SEGSIZE,
211             readonly => 0,
212             debug => 0,
213             };
214              
215 4 50       23 croak("odd number of arguments for object construction")
216             if(@_ % 2);
217 4         19 for(my $x = 0; $x <= $#_; $x += 2)
218             {
219 0 0       0 croak("Unknown parameter $_[$x] in $pkg object creation")
220             unless(exists($options->{lc($_[$x])}));
221 0         0 $options->{lc($_[$x])} = $_[($x + 1)];
222             }
223              
224 4 50       16 _init_dumper() if($options->{debug});
225              
226 4 50       16 if($options->{rootname})
227             {
228 0         0 carp('obsolete parameter: rootname');
229             # delete rootname parameter and if rootkey is undefined, copy the old rootname value in it.
230 0 0       0 (defined $options->{rootkey} ? my $devnull : $options->{rootkey}) = delete($options->{rootname});
231             }
232              
233 4 50       37 $options->{rootkey} = defined($options->{rootkey}) ? $options->{rootkey} : $self->_get_rootkey;
234              
235 4         13 foreach my $name (qw(namespace rootkey))
236             {
237 8 50 33     55 croak("$pkg object creation missing $name parameter.")
238             unless(defined($options->{$name}) && $options->{$name} ne '');
239             }
240              
241 4 0       17 $self->_debug("create Apache::SharedMem instence. options: ", join(', ', map("$_ => " . (defined($options->{$_}) ? $options->{$_} : 'UNDEF'), keys %$options)))
    50          
242             if($options->{debug});
243              
244 4 0 0     19 $self->_init_namespace || $options->{readonly} || return undef;
245              
246 0         0 return $self;
247             }
248              
249             =pod
250              
251             =head2 get (key, [wait, [timeout]])
252              
253             my $var = $object->get('mykey', WAIT, 50);
254             if($object->status & FAILURE)
255             {
256             die("can't get key 'mykey´: " . $object->error);
257             }
258              
259             =over 4
260              
261             =item *
262              
263             C required, string
264              
265             This is the name of elemenet that you want get from the shared namespace. It can be any string that perl
266             support for hash's key.
267              
268             =item *
269              
270             C optional
271              
272             Defined the locking status of the request. If you must get the value, and can't continue without it, set
273             this argument to constant WAIT, unless you can set it to NOWAIT.
274              
275             If the key is locked when you are tring to get the value, NOWAIT return status FAILURE, and WAIT hangup
276             until the value is unlocked. An alternative is to setup a WAIT timeout, see below.
277              
278             NOTE: you needs :wait tag import:
279              
280             use Apache::SharedMem qw(:wait)
281              
282             timeout (optional) integer:
283              
284             if WAIT is on, timeout setup the number of seconds to wait for a blocking lock, usefull for preventing
285             dead locks.
286              
287             =back
288              
289             Following status can be set (needs :status tag import):
290              
291             SUCCESS FAILURE
292              
293             On error, method return undef(), but undef() is also a valid answer, so don't test the method status
294             by this way, use ($obj->status & SUCCESS) instead.
295              
296             =cut
297              
298             sub get
299             {
300 0   0 0 1 0 my $self = shift || croak('invalide method call');
301 0 0 0     0 my $key = defined($_[0]) && $_[0] ne '' ? shift : croak(defined($_[0]) ? 'Not enough arguments for get method' : 'Invalid argument "" for get method');
    0          
302 0 0       0 my $wait = defined($_[0]) ? shift : (shift, 1);
303 0         0 my $timeout = shift;
304 0 0       0 croak('Too many arguments for get method') if(@_);
305 0         0 $self->_unset_error;
306            
307 0 0       0 $self->_debug("$key ", $wait ? '(wait)' : '(no wait)');
308              
309 0 0       0 my($lock_success, $out_lock) = $self->_smart_lock(($wait ? LOCK_SH : LOCK_SH|LOCK_NB), $timeout);
310 0 0       0 unless($lock_success)
311             {
312 0         0 $self->_set_error('can\'t get shared lock for "get" method');
313 0         0 $self->_set_status(FAILURE);
314 0         0 return(undef());
315             }
316              
317             # extract datas from the shared memory
318 0         0 my $share = $self->_get_namespace;
319              
320 0         0 $self->lock($out_lock, $timeout);
321              
322 0 0       0 if(exists $share->{$key})
323             {
324 0         0 $self->_set_status(SUCCESS);
325 0         0 return($share->{$key}); # can be undef() !
326             }
327             else
328             {
329 0         0 $self->_set_status(FAILURE);
330 0         0 $self->_set_error("can't get key $key, it doesn't exists");
331 0         0 return(undef());
332             }
333             }
334              
335             =pod
336              
337             =head2 set (key, value, [wait, [timeout]])
338              
339             my $rv = $object->set('mykey' => 'somevalue');
340             if($object->status eq FAILURE)
341             {
342             die("can't set key 'mykey´: " . $object->error);
343             }
344              
345             Try to set element C to C from the shared segment.
346              
347             =over 4
348              
349             =item *
350              
351             C required
352              
353             name of place where to store the value
354              
355             =item *
356              
357             C required
358              
359             data to store
360              
361             =item *
362              
363             C optional
364              
365             WAIT or NOWAIT (default WAIT) make or not a blocking shared lock (need :wait tag import).
366              
367             =item *
368              
369             C optional
370              
371             if WAIT is on, timeout setup the number of seconds to wait for a blocking lock (usefull for preventing dead locks)
372              
373             =back
374              
375             return status: SUCCESS FAILURE
376              
377             =cut
378              
379             sub set
380             {
381 0   0 0 1 0 my $self = shift || croak('invalid method call');
382 0 0 0     0 my $key = defined($_[0]) && $_[0] ne '' ? shift : croak(defined($_[0]) ? 'Not enough arguments for set method' : 'Invalid argument "" for set method');
    0          
383 0 0       0 my $value = defined($_[0]) ? shift : croak('Not enough arguments for set method');
384 0 0       0 my $wait = defined($_[0]) ? shift : (shift, 1);
385 0         0 my $timeout = shift;
386 0 0       0 croak('Too many arguments for set method') if(@_);
387 0         0 $self->_unset_error;
388            
389 0 0       0 $self->_debug("$key $value ", $wait ? '(wait)' : '(no wait)');
390              
391 0 0       0 my($lock_success, $out_lock) = $self->_smart_lock(($wait ? LOCK_EX : LOCK_EX|LOCK_NB), $timeout);
392 0 0       0 unless($lock_success)
393             {
394 0         0 $self->_set_error('can\'t get exclusive lock for "set" method');
395 0         0 $self->_set_status(FAILURE);
396 0         0 return(undef());
397             }
398              
399 0         0 my $share = $self->_get_namespace;
400 0         0 $share->{$key} = $value;
401 0         0 $self->_store_namespace($share);
402              
403 0         0 $self->lock($out_lock, $timeout);
404              
405 0         0 $self->_set_status(SUCCESS);
406             # return value, like a common assigment
407 0         0 return($value);
408             }
409              
410             =pod
411              
412             =head2 delete (key, [wait, [timeout]])
413              
414             =cut
415              
416             sub delete
417             {
418 0     0 1 0 my $self = shift;
419 0 0       0 my $key = defined($_[0]) ? shift : croak('Not enough arguments for delete method');
420 0 0       0 my $wait = defined($_[0]) ? shift : (shift, 1);
421 0         0 my $timeout = shift;
422 0 0       0 croak('Too many arguments for delete method') if(@_);
423 0         0 $self->_unset_error;
424              
425 0 0       0 $self->_debug("$key ", $wait ? '(wait)' : '(no wait)');
426              
427 0         0 my $exists = $self->exists($key, $wait, $timeout);
428 0 0       0 if(!defined $exists)
    0          
429             {
430 0         0 $self->_set_error("can\'t delete key '$key': ", $self->error);
431 0         0 $self->_set_status(FAILURE);
432 0         0 return(undef());
433             }
434             elsif(!$exists)
435             {
436 0         0 $self->_debug("DELETE[$$]: key '$key' wasn't exists");
437 0         0 $self->_set_status(FAILURE);
438 0         0 return(undef());
439             }
440              
441 0 0       0 my($lock_success, $out_lock) = $self->_smart_lock(($wait ? LOCK_EX : LOCK_EX|LOCK_NB), $timeout);
442 0 0       0 unless($lock_success)
443             {
444 0         0 $self->_set_error('can\'t get exclusive lock for "delete" method');
445 0         0 $self->_set_status(FAILURE);
446 0         0 return(undef());
447             }
448              
449              
450 0         0 my $share = $self->_get_namespace;
451 0         0 my $rv = delete($share->{$key});
452 0         0 $self->_store_namespace($share);
453            
454 0         0 $self->lock($out_lock, $timeout);
455              
456 0         0 $self->_set_status(SUCCESS);
457             # like a real delete
458 0         0 return($rv);
459             }
460              
461             =pod
462              
463             =head2 exists (key, [wait, [timeout]])
464              
465             =cut
466              
467             sub exists
468             {
469 0     0 1 0 my $self = shift;
470 0 0       0 my $key = defined($_[0]) ? shift : croak('Not enough arguments for exists method');
471 0 0       0 my $wait = defined($_[0]) ? shift : (shift, 1);
472 0         0 my $timeout = shift;
473 0 0       0 croak('Too many arguments for exists method') if(@_);
474 0         0 $self->_unset_error;
475              
476 0         0 $self->_debug("key: $key");
477              
478 0 0       0 my($lock_success, $out_lock) = $self->_smart_lock(($wait ? LOCK_SH : LOCK_SH|LOCK_NB), $timeout);
479 0 0       0 unless($lock_success)
480             {
481 0         0 $self->_set_error('can\'t get shared lock for "exists" method');
482 0         0 $self->_set_status(FAILURE);
483 0         0 return(undef());
484             }
485              
486 0         0 my $share = $self->_get_namespace;
487              
488 0         0 $self->lock($out_lock, $timeout);
489              
490 0         0 $self->_set_status(SUCCESS);
491 0         0 return(exists $share->{$key});
492             }
493              
494             =pod
495              
496             =head2 firstkey ([wait, [timeout]])
497              
498             =cut
499              
500             sub firstkey
501             {
502 0     0 1 0 my $self = shift;
503 0 0       0 my $wait = defined($_[0]) ? shift : (shift, 1);
504 0         0 my $timeout = shift;
505 0 0       0 croak('Too many arguments for firstkey method') if(@_);
506 0         0 $self->_unset_error;
507              
508 0 0       0 my($lock_success, $out_lock) = $self->_smart_lock(($wait ? LOCK_SH : LOCK_SH|LOCK_NB), $timeout);
509 0 0       0 unless($lock_success)
510             {
511 0         0 $self->_set_error('can\'t get shared lock for "firstkey" method');
512 0         0 $self->_set_status(FAILURE);
513 0         0 return(undef());
514             }
515              
516 0         0 my $share = $self->_get_namespace;
517              
518 0         0 $self->lock($out_lock, $timeout);
519            
520 0         0 my $firstkey = (keys(%$share))[0];
521 0         0 $self->_set_status(SUCCESS);
522 0         0 return($firstkey, $share->{$firstkey});
523             }
524              
525             =pod
526              
527             =head2 nextkey (lastkey, [wait, [timeout]])
528              
529             =cut
530              
531             sub nextkey
532             {
533 0     0 1 0 my $self = shift;
534 0 0       0 my $lastkey = defined($_[0]) ? shift : croak('Not enough arguments for nextkey method');
535 0 0       0 my $wait = defined($_[0]) ? shift : (shift, 1);
536 0         0 my $timeout = shift;
537 0 0       0 croak('Too many arguments for nextkey method') if(@_);
538 0         0 $self->_unset_error;
539              
540 0 0       0 my($lock_success, $out_lock) = $self->_smart_lock(($wait ? LOCK_SH : LOCK_SH|LOCK_NB), $timeout);
541 0 0       0 unless($lock_success)
542             {
543 0         0 $self->_set_error('can\'t get shared lock for "nextkey" method');
544 0         0 $self->_set_status(FAILURE);
545 0         0 return(undef());
546             }
547              
548 0         0 my $share = $self->_get_namespace;
549              
550 0         0 $self->lock($out_lock, $timeout);
551            
552 0         0 $self->_set_status(SUCCESS);
553 0         0 my @keys = keys %share;
554 0         0 for(my $x = 0; $x < $#keys; $x++)
555             {
556 0 0       0 return($share->{$keys[$x+1]}) if($share->{$keys[$x]} eq $lastkey);
557             }
558 0         0 return(undef());
559             }
560              
561             =pod
562              
563             =head2 clear ([wait, [timeout]])
564              
565             return 0 on error
566              
567             =cut
568              
569             sub clear
570             {
571 0     0 1 0 my $self = shift;
572 0 0       0 my $wait = defined($_[0]) ? shift : (shift, 1);
573 0         0 my $timeout = shift;
574 0 0       0 croak('Too many arguments for clear method') if(@_);
575 0         0 $self->_unset_error;
576              
577 0 0       0 my($lock_success, $out_lock) = $self->_smart_lock(($wait ? LOCK_EX : LOCK_EX|LOCK_NB), $timeout);
578 0 0       0 unless($lock_success)
579             {
580 0         0 $self->_set_error('can\'t get shared lock for "clear" method');
581 0         0 $self->_set_status(FAILURE);
582 0         0 return(0);
583             }
584              
585 0         0 $self->_store_namespace({});
586              
587 0         0 $self->lock($out_lock, $timeout);
588            
589 0         0 $self->_set_status(SUCCESS);
590 0         0 return(undef());
591             }
592              
593             =pod
594              
595             =head2 release [namespace]
596              
597             Release share memory space taken by the given namespace or object's namespace. Root map will be release too if empty.
598              
599             =cut
600              
601             sub release
602             {
603 0     0 1 0 my $self = shift;
604 0         0 my $options = $self->{options};
605 0 0       0 my $namespace = defined $_[0] ? shift : $options->{namespace};
606 0         0 $self->_unset_error;
607              
608 0         0 $self->_debug($namespace);
609              
610 0 0       0 if($options->{readonly})
611             {
612 0         0 $self->_set_error('can\'t call release namespace on readonly mode');
613 0         0 $self->_set_status(FAILURE);
614 0         0 return undef;
615             }
616              
617 0         0 $self->_root_lock(LOCK_EX);
618 0         0 my $root = $self->_get_root;
619              
620 0 0       0 unless(exists $root->{'map'}->{$namespace})
621             {
622 0         0 $self->_set_error("Apache::SharedMem: namespace '$namespace' doesn't exists in the map");
623 0         0 $self->_set_status(FAILURE);
624 0         0 return(undef());
625             }
626              
627 0         0 my $properties = delete($root->{'map'}->{$namespace});
628              
629 0         0 $self->_store_root($root);
630 0         0 $self->_root_unlock;
631              
632 0         0 delete($self->{namespace});
633              
634 0         0 my $share = new IPC::ShareLite
635             (
636             -key => $properties->{key},
637             -size => $properties->{size},
638             -mode => $properties->{mode},
639             -create => 0,
640             -destroy => 1,
641             );
642 0 0       0 unless(defined $share)
643             {
644 0         0 $self->_set_error("Apache::SharedMem: unable to get shared cache block: $!");
645 0         0 $self->_set_status(FAILURE);
646 0         0 return(undef());
647             }
648              
649 0 0       0 unless(keys %{$root->{'map'}})
  0         0  
650             {
651             # map is empty, destroy it
652 0         0 $self->_debug("root map is empty, delete it");
653 0         0 undef($self->{root});
654 0         0 my $rm = new IPC::ShareLite
655             (
656             -key => $options->{rootkey},
657             -size => $options->{ipc_segsize},
658             -mode => $options->{ipc_mode},
659             -create => 0,
660             -destroy => 1
661             );
662 0 0       0 unless(defined $rm)
663             {
664 0         0 $self->_set_status(FAILURE);
665 0         0 $self->_set_error("can't delete empty root map: $!");
666             }
667 0         0 undef $rm; # call DESTROY method explicitly
668             }
669              
670 0         0 $self->_set_status(SUCCESS);
671 0         0 return(1);
672             }
673              
674             =pod
675              
676             =head2 destroy
677              
678             Destroy all namespace found in the root map, and root map itself.
679              
680             =cut
681              
682             sub destroy
683             {
684 0     0 1 0 my $self = shift;
685              
686 0         0 $self->_root_lock(LOCK_SH);
687 0         0 my $root = $self->_get_root;
688 0         0 $self->_root_unlock;
689              
690 0         0 my @ns_list = keys(%{$root->{'map'}});
  0         0  
691 0         0 $self->_debug('segment\'s list for deletion : ', join(', ', @ns_list));
692 0         0 my $err = 0;
693 0         0 foreach $ns (@ns_list)
694             {
695 0         0 $self->_debug("release namespace: $ns");
696 0         0 $self->release($ns);
697 0 0       0 $err++ unless($self->status & SUCCESS);
698             }
699 0 0       0 $self->_set_status($err ? FAILURE : SUCCESS);
700             }
701              
702             =pod
703              
704             =head2 size ([wait, [timeout]])
705              
706             =cut
707              
708             sub size
709             {
710 0     0 1 0 my $self = shift;
711 0 0       0 my $wait = defined($_[0]) ? shift : (shift, 1);
712 0         0 my $timeout = shift;
713 0 0       0 croak('Too many arguments for size method') if(@_);
714 0         0 $self->_unset_error;
715              
716 0 0       0 my($lock_success, $out_lock) = $self->_smart_lock(($wait ? LOCK_SH : LOCK_SH|LOCK_NB), $timeout);
717 0 0       0 unless($lock_success)
718             {
719 0         0 $self->_set_error('can\'t get shared lock for "size" method');
720 0         0 $self->_set_status(FAILURE);
721 0         0 return(undef());
722             }
723              
724 0         0 my $serialized;
725 0         0 eval { $serialized = $self->{namespace}->fetch(); };
  0         0  
726 0 0       0 confess("Apache::SharedMem: Problem fetching segment. IPC::ShareLite error: $@") if $@;
727 0 0       0 confess("Apache::SharedMem: Problem fetching segment. IPC::ShareLite error: $!") unless(defined $serialized);
728              
729 0         0 $self->lock($out_lock, $timeout);
730              
731 0         0 $self->_set_status(SUCCESS);
732 0         0 return(length $serialized);
733             }
734              
735             =pod
736              
737             =head2 namespaces
738              
739             Debug method, return the list of all namespace in the root map.
740             (devel only)
741              
742             =cut
743              
744             sub namespaces
745             {
746 0     0 1 0 my $self = shift;
747 0         0 my $record = $self->_get_root;
748 0         0 return(keys %{$record->{'map'}});
  0         0  
749             }
750              
751             sub dump_map
752             {
753 0     0 0 0 my $self = shift;
754              
755 0         0 _init_dumper();
756 0   0     0 my $root_record = $self->_get_root || return undef;
757 0         0 return Data::Dumper::Dumper($root_record);
758             }
759              
760             sub dump
761             {
762 0     0 0 0 my $self = shift;
763 0 0       0 my $namespace = defined $_[0] ? shift : croak('too few arguments');
764              
765 0         0 _init_dumper();
766 0 0       0 if(my $ns_obj = $self->_get_namespace_ipcobj($self->_get_root, $namespace))
767             {
768 0         0 return Data::Dumper::Dumper($self->_get_record($ns_obj));
769             }
770             else
771             {
772 0         0 carp("can't read namespace $namespace: ", $self->error);
773 0         0 return undef;
774             }
775             }
776              
777             =pod
778              
779             =head2 lock ([lock_type, [timeout]])
780              
781             get a lock on the share segment. It returns C if failed, 1 if successed.
782              
783             =over 4
784              
785             =item *
786              
787             C optional
788              
789             type of lock (LOCK_EX, LOCK_SH, LOCK_NB, LOCK_UN)
790              
791             =item *
792              
793             C optional
794              
795             time to wait for an exclusive lock before aborting
796              
797             =back
798              
799             return status: FAILURE SUCCESS
800              
801             =cut
802              
803             sub lock
804             {
805 0     0 1 0 my($self, $type, $timeout) = @_;
806 0 0       0 $self->_debug("type ", (defined $type ? $type : 'default'), defined $timeout ? ", timeout $timeout" : '');
    0          
807 0         0 my $rv = $self->_lock($type, $timeout, $self->{namespace});
808             # we keep a trace of the actual lock status for smart lock mecanisme
809 0 0       0 $self->{_lock_status} = $type if($self->status eq SUCCESS);
810 0         0 return($rv);
811             }
812              
813 0 0   0   0 sub _root_lock { $_[0]->_debug("type $_[1]", defined $_[2] ? ", timeout $_[2]" : ''); $_[0]->_lock($_[1], $_[2], $_[0]->{root}) }
  0         0  
814              
815             sub _lock
816             {
817 0 0   0   0 confess('Apache::SharedMem: Not enough arguments for lock method') if(@_ < 3);
818 0         0 my($self, $type, $timeout, $ipc_obj) = @_;
819 0         0 $self->_unset_error;
820              
821 0 0 0     0 $timeout = 0 if(!defined $timeout || $timeout =~ /\D/ || $timeout < 0);
      0        
822 0 0 0     0 return($self->unlock) if(defined $type && $type eq LOCK_UN); # strang bug, LOCK_UN, seem not to be same as unlock for IPC::ShareLite...
823              
824             # get a lock
825 0         0 my $rv;
826             eval
827 0         0 {
828 0     0   0 local $SIG{ALRM} = sub {die "timeout"};
  0         0  
829 0         0 alarm $timeout;
830 0 0       0 $rv = $ipc_obj->lock(defined $type ? $type : LOCK_EX);
831 0         0 alarm 0;
832             };
833 0 0 0     0 if($@ || !$rv)
834             {
835 0         0 $self->_set_error("Can\'t lock get lock: $!$@");
836 0         0 $self->_set_status(FAILURE);
837 0         0 return(undef());
838             };
839 0         0 $self->_set_status(SUCCESS);
840 0         0 return(1);
841             }
842              
843             =pod
844              
845             =head2 unlock
846              
847             freeing a lock
848              
849             =cut
850              
851             sub unlock
852             {
853 0     0 1 0 my $self = shift;
854 0         0 $self->_debug;
855 0         0 my $rv = $self->_unlock($self->{namespace});
856 0 0       0 $self->{_lock_status} = LOCK_UN if($rv);
857 0         0 return($rv);
858             }
859 0     0   0 sub _root_unlock { $_[0]->_debug; $_[0]->_unlock($_[0]->{root}) }
  0         0  
860              
861             sub _unlock
862             {
863 0     0   0 my($self, $ipc_obj) = @_;
864 0         0 $self->_unset_error;
865              
866             $ipc_obj->unlock or
867             do
868 0 0       0 {
869 0         0 $self->_set_error("Can't unlock segment");
870 0         0 $self->_set_status(FAILURE);
871 0         0 return(undef());
872             };
873 0         0 $self->_set_status(SUCCESS);
874 0         0 return(1);
875             }
876              
877             =pod
878              
879             =head2 error
880              
881             return the last error message that happened.
882              
883             =cut
884              
885 0     0 1 0 sub error { return($_[0]->{__last_error__}); }
886              
887             =pod
888              
889             =head2 status
890              
891             Return the last called method status. This status should be used with bitmask operators
892             &, ^, ~ and | like this :
893              
894             # is last method failed ?
895             if($object->status & FAILURE) {something to do on failure}
896              
897             # is last method don't succed ?
898             if($object->status ^ SUCCESS) {something to do on failure}
899              
900             It's not recommended to use equality operator (== and !=) or (eq and ne), they may don't
901             work in future versions.
902              
903             To import status' constants, you have to use the :status import tag, like below :
904              
905             use Apache::SharedMem qw(:status);
906              
907             =cut
908              
909 0     0 1 0 sub status { return($_[0]->{__status__}); }
910              
911             sub _smart_lock
912             {
913             # this method try to implement a smart fashion to manage locks.
914             # problem is when user place manually a lock before a get, set,... call. the
915             # methode handle his own lock, and in this code :
916             # $share->lock(LOCK_EX);
917             # my $var = $share->get(key);
918             # ...make traitement on $var
919             # $share->set(key=>$var);
920             # $share->unlock;
921             #
922             # in this example, the first "get" call, change the lock for a share lock, and free
923             # the lock at the return.
924             #
925 0     0   0 my($self, $type, $timeout) = @_;
926            
927 0 0 0     0 if(!defined($self->{_lock_status}) || $self->{_lock_status} & LOCK_UN)
    0 0        
928             {
929             # no lock have been set, act like a normal lock
930 0         0 $self->_debug("locking type $type, return LOCK_UN");
931 0         0 return($self->lock($type, $timeout), LOCK_UN);
932             }
933             elsif(($self->{_lock_status} & LOCK_SH) && ($type & LOCK_EX))
934             {
935             # the current lock is powerless than targeted lock type
936 0         0 my $old_lock = $self->{_lock_status};
937 0         0 $self->_debug("locking type $type, return $old_lock");
938 0         0 return($self->lock($type, $timeout), $old_lock);
939             }
940              
941 0         0 $self->_debug("live lock untouch, return $self->{_lock_status}");
942 0         0 return(1, $self->{_lock_status});
943             }
944              
945             sub _init_root
946             {
947 4     4   6 my $self = shift;
948 4         9 my $options = $self->{options};
949 4         5 my $record;
950              
951 4         11 $self->_debug;
952             # try to get a handle on an existing root for this namespace
953 4         71 my $root = new IPC::ShareLite
954             (
955             -key => $options->{rootkey},
956             -mode => $options->{ipc_mode},
957             -size => $options->{ipc_segment_size},
958             -create => 0,
959             -destroy => 0,
960             );
961              
962 0 0       0 if(defined $root)
963             {
964             # we have found an existing root
965 0         0 $self->{root} = $root;
966 0         0 $self->_root_lock(LOCK_SH);
967 0         0 $record = $self->_get_root;
968 0         0 $self->_root_unlock;
969 0 0 0     0 unless(ref $record && ref($record) eq 'HASH' && exists $record->{'map'})
      0        
970             {
971 0 0       0 $self->_debug("map dump: ", $record, Data::Dumper::Dumper($record)) if($options->{debug});
972 0         0 confess("Apache::SharedMem object initialization: wrong root map type")
973             }
974              
975             # checking map version
976 0 0 0     0 unless(exists $record->{'version'} && $record->{'version'} >= 2)
977             {
978             # old map style, we ne upgrade it
979 0         0 $self->_root_lock(LOCK_EX);
980 0         0 foreach my $namespace (keys %{$record->{'map'}})
  0         0  
981             {
982 0         0 $namespace =
983             {
984             key => $namespace,
985             mode => $options->{ipc_mode},
986             size => $options->{ipc_segment_size},
987             }
988             }
989 0         0 $self->_store_root($record);
990 0         0 $self->_root_unlock;
991             }
992              
993 0         0 return($record);
994             }
995              
996 0         0 $self->_debug('root map first initalisation');
997              
998 0 0       0 if($options->{readonly})
999             {
1000 0         0 $self->_set_error("root map ($options->{rootkey}) doesn't exists, can't create one in readonly mode");
1001 0         0 $self->_set_status(FAILURE);
1002 0         0 return(undef);
1003             }
1004              
1005             # prepare empty root record for new root creation
1006             $record =
1007             {
1008 0         0 'map' => {},
1009             'last_key' => $options->{rootkey},
1010             'version' => 2, # map version
1011             };
1012              
1013 0         0 $root = new IPC::ShareLite
1014             (
1015             -key => $options->{rootkey},
1016             -mode => $options->{ipc_mode},
1017             -size => $options->{ipc_segment_size},
1018             -create => 1,
1019             -exclusive => 1,
1020             -destroy => 0,
1021             );
1022 0 0       0 confess("Apache::SharedMem object initialization: Unable to initialize root ipc shared memory segment ($options->{rootkey}): $!")
1023             unless(defined $root);
1024              
1025 0         0 $self->{root} = $root;
1026 0         0 $self->_root_lock(LOCK_EX);
1027 0         0 $self->_store_root($record);
1028 0         0 $self->_root_unlock;
1029              
1030 0         0 return($record);
1031             }
1032              
1033             sub _get_namespace_ipcobj
1034             {
1035 0     0   0 my($self, $rootrecord, $namespace) = @_;
1036              
1037 0 0       0 if(my $properties = $rootrecord->{'map'}->{$namespace})
1038             {
1039 0         0 $self->_debug('namespace exists');
1040             # namespace already exists
1041 0         0 $share = new IPC::ShareLite
1042             (
1043             -key => $properties->{key},
1044             -mode => $properties->{mode},
1045             -size => $properties->{size},
1046             -create => 0,
1047             -destroy => 0,
1048             );
1049 0 0       0 confess("Apache::SharedMem: Unable to get shared cache block ($namespace=$properties->{key}): $!") unless(defined $share);
1050 0         0 $self->_set_status(SUCCESS);
1051 0         0 return $share;
1052             }
1053             else
1054             {
1055 0         0 $self->_set_status(FAILURE);
1056 0         0 $self->_set_error("no such namespace: '$namespace'");
1057 0         0 return undef();
1058             }
1059             }
1060              
1061             sub _init_namespace
1062             {
1063 4     4   24 my $self = shift;
1064 4         8 my $options = $self->{options};
1065 4         9 my $namespace = $options->{namespace};
1066              
1067 4         13 $self->_debug;
1068 4   0     32 my $rootrecord = $self->_init_root || return undef;
1069              
1070 0         0 my $share;
1071 0 0       0 if(exists $rootrecord->{'map'}->{$namespace})
1072             {
1073 0         0 $share = $self->_get_namespace_ipcobj($rootrecord, $namespace);
1074             }
1075             else
1076             {
1077 0 0       0 if($options->{readonly})
1078             {
1079 0         0 $self->_set_error("namespace '$namespace' doesn't exists, can't create one in readonly mode");
1080 0         0 $self->_set_status(FAILURE);
1081 0         0 return(undef);
1082             }
1083              
1084 0         0 $self->_debug('namespace doesn\'t exists, creating...');
1085             # otherwise we need to find a new segment
1086 0         0 my $ipc_key = $rootrecord->{'last_key'}+1;
1087 0         0 my $ipc_mode = $options->{ipc_mode};
1088 0         0 my $ipc_size = $options->{ipc_segment_size};
1089 0         0 for(my $end = $ipc_key + 10_000; $ipc_key != $end; $ipc_key++)
1090             {
1091 0         0 $share = new IPC::ShareLite
1092             (
1093             -key => $ipc_key,
1094             -mode => $ipc_mode,
1095             -size => $ipc_size,
1096             -create => 1,
1097             -exclusive => 1,
1098             -destroy => 0,
1099             );
1100 0 0       0 last if(defined $share);
1101             }
1102 0 0       0 croak("Apache::SharedMem: searched through 10,000 consecutive locations for a free shared memory segment, giving up: $!")
1103             unless(defined $share);
1104              
1105             # update the root record
1106 0         0 $self->_root_lock(LOCK_EX);
1107 0         0 $rootrecord->{'map'}->{$namespace} =
1108             {
1109             key => $ipc_key,
1110             mode => $ipc_mode,
1111             size => $ipc_size,
1112             };
1113 0         0 $rootrecord->{'last_key'} = $ipc_key;
1114 0         0 $self->_store_record({}, $share); # init contents, to avoid root map's corruption in certain circumstances
1115 0         0 $self->_store_root($rootrecord);
1116 0         0 $self->_root_unlock;
1117             }
1118              
1119 0         0 return($self->{namespace} = $share);
1120             }
1121              
1122             # return a most hase possible, unique IPC identifier
1123             sub _get_rootkey
1124             {
1125 4     4   8 my $self = shift;
1126 4         7 my($ipckey, $docroot, $uid);
1127              
1128 4 50 33     41 if(exists $ENV{'GATEWAY_INTERFACE'} && $ENV{'GATEWAY_INTERFACE'} =~ /^CGI-Perl/)
    50          
1129             {
1130             # we are under mod_perl
1131 0 0       0 if(defined $Apache::SharedMem::ROOTKEY)
1132             {
1133 0         0 $ipckey = $Apache::SharedMem::ROOTKEY; # look at import() for more details
1134             }
1135             else
1136             {
1137 0         0 require Apache;
1138 0 0 0     0 if(defined $Apache::Server::Starting && $Apache::Server::Starting)
1139             {
1140             # we are in the startup.pl
1141 0         0 my $s = Apache->server;
1142 0 0       0 $docroot = $s->dir_config->get('PROJECT_DOCUMENT_ROOT')
1143             ? ($s->dir_config->get('PROJECT_DOCUMENT_ROOT'))[-1] : return undef;
1144 0 0       0 $uid = $s->dir_config->get('PROJECT_ID')
1145             ? ($s->dir_config->get('PROJECT_ID'))[-1] : $s->uid;
1146             }
1147             else
1148             {
1149 0         0 my $r = Apache->request;
1150 0         0 my $s = $r->server;
1151 0         0 $docroot = $r->document_root;
1152 0         0 $uid = $s->uid;
1153             }
1154             }
1155             }
1156             elsif(exists $ENV{'DOCUMENT_ROOT'})
1157             {
1158             # we are under mod_cgi
1159 0         0 $docroot = $ENV{DOCUMENT_ROOT};
1160 0         0 $uid = $<;
1161             }
1162             else
1163             {
1164             # we are in an undefined environment
1165 4         13 $docroot = $ENV{PWD};
1166 4         80 $uid = $<;
1167             }
1168              
1169 4 50       21 unless(defined $ipckey)
1170             {
1171 4 0 33     20 confess("PROJECT_DOCUMENT_ROOT doesn't exists or can't be accessed: " . (defined $docroot ? $docroot : '[undefined]'))
    0 0        
      33        
1172             if(not defined $docroot || $docroot eq '' || not -e $docroot || not -r $docroot);
1173 4 0 33     19 confess("PROJECT_ID is not numeric: " . (defined $uid ? $uid : '[undefined]'))
    50          
1174             if(not defined $uid || $uid =~ /[^\d\-]/);
1175 4         203 $ipckey = IPC::SysV::ftok($docroot, $uid);
1176             }
1177              
1178 4 50       46 $self->_debug("document_root=$docroot, uid=$uid, rootkey=$ipckey") if(defined $self);
1179 4         12 return($ipckey);
1180             }
1181              
1182 0     0   0 sub _get_namespace { $_[0]->_debug; $_[0]->_get_record($_[0]->{namespace}) }
  0         0  
1183 0     0   0 sub _get_root { $_[0]->_debug; $_[0]->_get_record($_[0]->{root}) }
  0         0  
1184              
1185             sub _get_record
1186             {
1187 0     0   0 my($self, $ipc_obj) = @_;
1188              
1189 0 0       0 return undef unless(defined $ipc_obj);
1190              
1191 0         0 my($serialized, $record);
1192              
1193             # fetch the shared block
1194 0         0 eval { $serialized = $ipc_obj->fetch(); };
  0         0  
1195 0 0       0 confess("Apache::SharedMem: Problem fetching segment. IPC::ShareLite error: $@") if $@;
1196 0 0       0 confess("Apache::SharedMem: Problem fetching segment. IPC::ShareLite error: $!") unless(defined $serialized);
1197              
1198 0         0 $self->_debug(4, 'storable src: ', $serialized);
1199              
1200 0 0       0 if($serialized ne '')
1201             {
1202             # thaw the shared block
1203 0         0 eval { $record = thaw($serialized) };
  0         0  
1204 0 0       0 confess("Apache::SharedMem: Invalid share block recieved from shared memory. Storable error: $@") if $@;
1205 0 0       0 confess("Apache::SharedMem: Invalid share block recieved from shared memory.") unless(ref($record) eq 'HASH');
1206             }
1207             else
1208             {
1209             # record not initialized
1210 0         0 $record = {};
1211             }
1212              
1213 0 0       0 $self->_debug(4, 'dump: ', Data::Dumper::Dumper($record)) if($self->{options}->{debug});
1214              
1215 0         0 return($record);
1216             }
1217              
1218 0     0   0 sub _store_namespace { $_[0]->_debug; $_[0]->_store_record($_[1], $_[0]->{namespace}) }
  0         0  
1219 0     0   0 sub _store_root { $_[0]->_debug; $_[0]->_store_record($_[1], $_[0]->{root}) }
  0         0  
1220              
1221             sub _store_record
1222             {
1223 0     0   0 my $self = shift;
1224 0 0       0 my $share = defined($_[0]) ? (ref($_[0]) eq 'HASH' ? shift() : croak('Apache::SharedMem: unexpected error, wrong data type')) : croak('Apache::SharedMem; unexpected error, missing argument');
    0          
1225 0 0       0 my $ipc_obj = defined $_[0] ? shift : return undef;
1226              
1227 0 0       0 if($self->{options}->{readonly})
1228             {
1229 0         0 $self->_set_error('can\'t store any data in readonly mode');
1230 0         0 $self->_set_status(FAILURE);
1231 0         0 return undef;
1232             }
1233              
1234 0 0       0 $self->_debug(4, 'dump: ', Data::Dumper::Dumper($share)) if($self->{options}->{debug});
1235              
1236 0         0 my $serialized;
1237              
1238             # freeze the shared block
1239 0         0 eval { $serialized = freeze($share) };
  0         0  
1240 0 0       0 confess("Apache::SharedMem: Problem while the serialization of shared data. Storable error: $@") if $@;
1241 0 0 0     0 confess("Apache::SahredMem: Problem while the serialization of shared data.") unless(defined $serialized && $serialized ne '');
1242              
1243 0         0 $self->_debug(4, 'storable src: ', $serialized);
1244              
1245             # store the serialized data
1246 0         0 eval { $ipc_obj->store($serialized) };
  0         0  
1247 0 0       0 confess("Apache::SharedMem: Problem storing share segment. IPC::ShareLite error: $@") if $@;
1248              
1249 0         0 return($share);
1250             }
1251              
1252             sub _debug
1253             {
1254 12 50   12   44 return() unless($_[0]->{options}->{debug});
1255 0         0 my $self = shift;
1256 0 0 0     0 my $dblvl = defined($_[0]) && $_[0] =~ /^\d$/ ? shift : 1;
1257 0 0       0 printf(STDERR "### DEBUG %s method(%s) pid[%s]: %s\n", (caller())[0], (split(/::/, (caller(1))[3]))[-1], $$, join('', @_)) if($self->{options}->{debug} >= $dblvl);
1258             }
1259              
1260             sub _set_error
1261             {
1262 0     0   0 my $self = shift;
1263 0         0 $self->_debug($Apache::SharedMem::ERROR = $self->{__last_error__} = join('', @_));
1264             }
1265              
1266             sub _unset_error
1267             {
1268 0     0   0 my $self = shift;
1269 0         0 $Apache::SharedMem::ERROR = $self->{__last_error__} = '';
1270             }
1271              
1272             sub _set_status
1273             {
1274 0     0   0 my $self = shift;
1275 0 0       0 $self->{__status__} = defined($_[0]) ? $_[0] : '';
1276 0         0 $self->_debug("setting status to $_[0]");
1277             }
1278              
1279             sub _init_dumper
1280             {
1281 0     0   0 require Data::Dumper;
1282 0         0 $Data::Dumper::Indent = 2;
1283 0         0 $Data::Dumper::Terse = 1;
1284 0         0 $Data::Dumper::Quotekeys = 0;
1285             }
1286              
1287             sub _cleanup
1288             {
1289 0 0   0   0 if(defined $Apache::SharedMem::ROOTKEY)
1290             {
1291 0         0 my $share = new Apache::SharedMem;
1292 0 0       0 $share->destroy if(defined $share)
1293             }
1294             }
1295              
1296             DESTROY
1297             {
1298             # auto unlock on destroy, it seem to work under mod_perl with Apache::Registry, not tested yet under mod_perl handlers
1299 4 0 0 4   2424 $_[0]->unlock
      33        
1300             if(defined $_[0]->{_lock_status} && ($_[0]->{_lock_status} & LOCK_SH || $_[0]->{_lock_status} & LOCK_EX));
1301             }
1302              
1303             1;
1304              
1305             =pod
1306              
1307             =head1 EXPORTS
1308              
1309             =head2 Default exports
1310              
1311             None.
1312              
1313             =head2 Available exports
1314              
1315             Following constant is available for exports : LOCK_EX LOCK_SH LOCK_UN LOCK_NB
1316             WAIT NOWAIT SUCCESS FAILURE
1317              
1318             =head2 Export tags defined
1319              
1320             The tag ":all" will get all of the above exports.
1321             Following tags are also available :
1322              
1323             =over 4
1324              
1325             =item
1326              
1327             :status
1328              
1329             Contents: SUCCESS FAILURE
1330              
1331             This tag is really recommended to the importation all the time.
1332              
1333             =item
1334              
1335             :lock
1336              
1337             Contents: LOCK_EX LOCK_SH LOCK_UN LOCK_NB
1338              
1339             =item
1340              
1341             :wait
1342              
1343             WAIT NOWAIT
1344              
1345             =back
1346              
1347             =head1 AUTHOR
1348              
1349             Olivier Poitrey Ers@rhapsodyk.netE
1350              
1351             =head1 LICENCE
1352              
1353             This program is free software; you can redistribute it and/or modify
1354             it under the terms of the GNU General Public License as published by
1355             the Free Software Foundation; either version 2 of the License, or (at
1356             your option) any later version.
1357              
1358             This program is distributed in the hope that it will be useful, but
1359             WITHOUT ANY WARRANTY; without even the implied warranty of
1360             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1361             General Public License for more details.
1362              
1363             You should have received a copy of the GNU General Public License
1364             along with the program; if not, write to the Free Software
1365             Foundation, Inc. :
1366              
1367             59 Temple Place, Suite 330, Boston, MA 02111-1307
1368              
1369             =head1 COPYRIGHT
1370              
1371             Copyright (C) 2001 - Olivier Poitrey
1372              
1373             =head1 PREREQUISITES
1374              
1375             Apache::SharedMem needs IPC::ShareLite, Storable both available from the CPAN.
1376              
1377             =head1 SEE ALSO
1378              
1379             L, L, L
1380              
1381             =head1 HISTORY
1382              
1383             $Log: SharedMem.pm,v $
1384             Revision 1.61 2001/10/04 12:15:22 rs
1385             Very major bugfix that made module unable to work correctly under mod_perl !
1386             New version 0.09 to CPAN immediatly
1387              
1388             Revision 1.60 2001/10/02 09:40:32 rs
1389             Bugfix in _get_rootkey private method: trap empty docroot or no read access
1390             to docroot error.
1391              
1392             Revision 1.59 2001/09/24 08:19:40 rs
1393             status now return bitmask values
1394              
1395             Revision 1.58 2001/09/21 14:45:30 rs
1396             little doc fixes
1397              
1398             Revision 1.57 2001/09/21 12:43:41 rs
1399             Change copyright
1400              
1401             Revision 1.56 2001/09/20 12:45:03 rs
1402             Documentation update: adding an EXPORTS section
1403              
1404             Revision 1.55 2001/09/19 14:19:41 rs
1405             made a trace more verbose
1406              
1407             Revision 1.54 2001/09/18 08:46:32 rs
1408             Documentation upgrade
1409              
1410             Revision 1.53 2001/09/17 14:56:41 rs
1411             Suppression of ROOTKEYS global hash, obsolete.
1412             Documentation update: USAGE => PROJECT_ID
1413              
1414             Revision 1.52 2001/08/29 15:54:01 rs
1415             little bug fix in _get_rootkey
1416              
1417             Revision 1.51 2001/08/29 14:28:08 rs
1418             add warning on no existing document_root in _get_rootkey
1419              
1420             Revision 1.50 2001/08/29 12:59:02 rs
1421             some documentation update.
1422             get method now return undef() if value is undefined.
1423              
1424             Revision 1.49 2001/08/29 08:30:32 rs
1425             syntax bugfix
1426              
1427             Revision 1.48 2001/08/29 08:27:13 rs
1428             doc fix
1429              
1430             Revision 1.47 2001/08/29 08:24:23 rs
1431             meny documentation updates
1432              
1433             Revision 1.46 2001/08/28 16:42:14 rs
1434             adding better support of mod_perl with a cleanup method handled to Apache's
1435             registry_cleanup.
1436              
1437             Revision 1.45 2001/08/28 10:17:00 rs
1438             little documentation fix
1439              
1440             Revision 1.44 2001/08/28 08:45:12 rs
1441             stop using autouse for Data::Dumper, mod_perl don't like it
1442             add auto unlock on DESTROY, seem to work under mod_perl with Apache::Registry
1443             TODO test with mod_perl handlers
1444              
1445             Revision 1.43 2001/08/27 15:42:02 rs
1446             bugfix in release method, on root map cleanup, ipc_mode must be defined
1447             bugfix in _init_namespace method, if object was create without any "set" called,
1448             the empty namespace won't be allocated.
1449              
1450             Revision 1.42 2001/08/24 16:11:25 rs
1451             - Implement a more efficient IPC key generation for the root segment, using
1452             the system ftok() function provied by IPC::SysV module
1453             - Pod documentation
1454             - Default IPC mode is now 0600
1455             - We now keep ipc_mode and ipc_segment_size in the root map for calling IPC::ShareLite
1456             with same values.
1457             - Add "readonly" parameter to constructor
1458             - Feature enhancement, add "dump" and "dump_map" methods
1459             - Data::Dumper is now autoused
1460             - Feature enhancement, release method now release root map when it go empty
1461             - Feature enhancement, add a "destroy" method, that call "release" method on all root-map's
1462             namespaces. Usefull for cleaning shared memory on Apache shutdown.
1463             - Misc bugfixes
1464              
1465             Revision 1.41 2001/08/23 08:37:03 rs
1466             major bug, _get_rootkey was call mod_perl method on a wrong object
1467              
1468             Revision 1.40 2001/08/23 08:08:18 rs
1469             little documentation update
1470              
1471             Revision 1.39 2001/08/23 00:56:32 rs
1472             vocabulary correction in POD documentation
1473              
1474             Revision 1.38 2001/08/22 16:10:15 rs
1475             - Pod documentation
1476             - Default IPC mode is now 0600
1477             - We now keep ipc_mode and ipc_segment_size in the root map for calling IPC::ShareLite
1478             with same values.
1479             - Bugfix, release now really clean segments (seem to be an IPC::ShareLite bug)
1480              
1481             Revision 1.37 2001/08/21 13:17:35 rs
1482             switch to version O.07
1483              
1484             Revision 1.36 2001/08/21 13:17:02 rs
1485             add method _get_rootkey. this method allow constructor to determine a more
1486             uniq ipc key. key is generated with IPC::SysV::ftok() function, based on
1487             ducument_root and user id.
1488              
1489             Revision 1.35 2001/08/17 13:28:18 rs
1490             make precedence more readable in "_set_status" method
1491             some pod corrections
1492              
1493             Revision 1.34 2001/08/08 14:15:07 rs
1494             forcing default lock to LOCK_EX
1495              
1496             Revision 1.33 2001/08/08 14:01:45 rs
1497             grrr syntax error second part, it's not my day.
1498              
1499             Revision 1.32 2001/08/08 13:59:01 rs
1500             syntax error introdius with the last fix
1501              
1502             Revision 1.31 2001/08/08 13:56:35 rs
1503             Starting version 0.06
1504             fixing an "undefined value" bug in lock methode
1505              
1506             Revision 1.30 2001/07/04 08:41:11 rs
1507             major documentation corrections
1508              
1509             Revision 1.29 2001/07/03 15:24:19 rs
1510             fix doc
1511              
1512             Revision 1.28 2001/07/03 14:53:02 rs
1513             make a real changes log
1514