File Coverage

blib/lib/IPC/Shareable.pm
Criterion Covered Total %
statement 120 537 22.3
branch 31 308 10.0
condition 8 49 16.3
subroutine 26 70 37.1
pod 16 16 100.0
total 201 980 20.5


line stmt bran cond sub pod time code
1             package IPC::Shareable;
2              
3 37     37   278272 use warnings;
  37         260  
  37         1047  
4 37     37   175 use strict;
  37         96  
  37         1134  
5              
6             require 5.00503;
7              
8 37     37   183 use Carp qw(croak confess carp);
  37         75  
  37         2367  
9 37     37   12552 use Data::Dumper;
  37         144863  
  37         2041  
10 37     37   14450 use IPC::Semaphore;
  37         200642  
  37         1094  
11 37     37   14068 use IPC::Shareable::SharedMem;
  37         89  
  37         1114  
12 37         1935 use IPC::SysV qw(
13             IPC_PRIVATE
14             IPC_CREAT
15             IPC_EXCL
16             IPC_NOWAIT
17             SEM_UNDO
18 37     37   193 );
  37         64  
19 37     37   19269 use JSON qw(-convert_blessed_universally);
  37         430636  
  37         188  
20 37     37   11140 use Scalar::Util;
  37         71  
  37         1989  
21 37     37   13326 use String::CRC32;
  37         14017  
  37         1998  
22 37     37   17771 use Storable 0.6 qw(freeze thaw);
  37         94585  
  37         3662  
23              
24             our $VERSION = '1.11';
25              
26             use constant {
27             LOCK_SH => 1,
28             LOCK_EX => 2,
29             LOCK_NB => 4,
30             LOCK_UN => 8,
31              
32 37   50     213955 DEBUGGING => ($ENV{SHAREABLE_DEBUG} or 0),
33              
34             SHM_BUFSIZ => 65536,
35             SEM_MARKER => 0,
36             SHM_EXISTS => 1,
37              
38             SHMMAX_BYTES => 1073741824, # 1 GB
39              
40             # Perl sends in a double as opposed to an integer to shmat(), and on some
41             # systems, this causes the IPC system to round down to the maximum integer
42             # size of 0x80000000 we correct that when generating keys with CRC32
43              
44             MAX_KEY_INT_SIZE => 0x80000000,
45              
46             EXCLUSIVE_CHECK_LIMIT => 10, # Number of times we'll check for existing segs
47 37     37   292 };
  37         68  
48              
49             require Exporter;
50             our @ISA = 'Exporter';
51             our @EXPORT_OK = qw(LOCK_EX LOCK_SH LOCK_NB LOCK_UN);
52             our %EXPORT_TAGS = (
53             all => [qw( LOCK_EX LOCK_SH LOCK_NB LOCK_UN )],
54             lock => [qw( LOCK_EX LOCK_SH LOCK_NB LOCK_UN )],
55             flock => [qw( LOCK_EX LOCK_SH LOCK_NB LOCK_UN )],
56             );
57             Exporter::export_ok_tags('all', 'lock', 'flock');
58              
59             # Locking scheme copied from IPC::ShareLite -- ltl
60             my %semop_args = (
61             (LOCK_EX),
62             [
63             1, 0, 0, # wait for readers to finish
64             2, 0, 0, # wait for writers to finish
65             2, 1, SEM_UNDO, # assert write lock
66             ],
67             (LOCK_EX|LOCK_NB),
68             [
69             1, 0, IPC_NOWAIT, # wait for readers to finish
70             2, 0, IPC_NOWAIT, # wait for writers to finish
71             2, 1, (SEM_UNDO | IPC_NOWAIT), # assert write lock
72             ],
73             (LOCK_EX|LOCK_UN),
74             [
75             2, -1, (SEM_UNDO | IPC_NOWAIT),
76             ],
77              
78             (LOCK_SH),
79             [
80             2, 0, 0, # wait for writers to finish
81             1, 1, SEM_UNDO, # assert shared read lock
82             ],
83             (LOCK_SH|LOCK_NB),
84             [
85             2, 0, IPC_NOWAIT, # wait for writers to finish
86             1, 1, (SEM_UNDO | IPC_NOWAIT), # assert shared read lock
87             ],
88             (LOCK_SH|LOCK_UN),
89             [
90             1, -1, (SEM_UNDO | IPC_NOWAIT), # remove shared read lock
91             ],
92             );
93              
94             my %default_options = (
95             key => IPC_PRIVATE,
96             create => 0,
97             exclusive => 0,
98             destroy => 0,
99             mode => 0666,
100             size => SHM_BUFSIZ,
101             protected => 0,
102             limit => 1,
103             graceful => 0,
104             warn => 0,
105             tidy => 0,
106             serializer => 'storable',
107             );
108              
109             my %global_register;
110             my %process_register;
111             my %used_ids;
112              
113             sub _trace;
114             sub _debug;
115              
116             # --- "Magic" methods
117             sub TIESCALAR {
118 2     2   1308 return _tie('SCALAR', @_);
119             }
120             sub TIEARRAY {
121 0     0   0 return _tie('ARRAY', @_);
122             }
123             sub TIEHASH {
124 0     0   0 return _tie('HASH', @_);
125             }
126             sub STORE {
127 0     0   0 my $knot = shift;
128              
129 0 0       0 if (! exists $global_register{$knot->seg->id}) {
130 0         0 $global_register{$knot->seg->id} = $knot;
131             }
132              
133 0 0       0 $knot->{_data} = $knot->_decode($knot->seg) unless ($knot->{_lock});
134              
135 0 0       0 if ($knot->{_type} eq 'HASH') {
    0          
    0          
136 0         0 my ($key, $val) = @_;
137 0 0       0 _mg_tie($knot, $val, $key) if $knot->_need_tie($val, $key);
138 0         0 $knot->{_data}{$key} = $val;
139             }
140             elsif ($knot->{_type} eq 'ARRAY') {
141 0         0 my ($i, $val) = @_;
142 0 0       0 _mg_tie($knot, $val, $i) if $knot->_need_tie($val, $i);
143 0         0 $knot->{_data}[$i] = $val;
144             }
145             elsif ($knot->{_type} eq 'SCALAR') {
146 0         0 my ($val) = @_;
147 0 0       0 _mg_tie($knot, $val) if $knot->_need_tie($val);
148 0         0 $knot->{_data} = \$val;
149             }
150             else {
151 0         0 croak "Variables of type $knot->{_type} not supported";
152             }
153              
154 0 0       0 if ($knot->{_lock} & LOCK_EX) {
155 0         0 $knot->{_was_changed} = 1;
156             } else {
157 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
158 0         0 croak "Could not write to shared memory: $!\n";
159             }
160             }
161              
162 0         0 return 1;
163             }
164             sub FETCH {
165 0     0   0 my $knot = shift;
166              
167 0 0       0 if (! exists $global_register{$knot->seg->id}) {
168 0         0 $global_register{$knot->seg->id} = $knot;
169             }
170              
171 0         0 my $data;
172 0 0 0     0 if ($knot->{_lock} || $knot->{_iterating}) {
173 0         0 $knot->{_iterating} = 0; # In case we break out
174 0         0 $data = $knot->{_data};
175             } else {
176 0         0 $data = $knot->_decode($knot->seg);
177 0         0 $knot->{_data} = $data;
178             }
179              
180 0         0 my $val;
181              
182 0 0       0 if ($knot->{_type} eq 'HASH') {
    0          
    0          
183 0 0       0 if (defined $data) {
184 0         0 my $key = shift;
185 0         0 $val = $data->{$key};
186             } else {
187 0         0 return;
188             }
189             }
190             elsif ($knot->{_type} eq 'ARRAY') {
191 0 0       0 if (defined $data) {
192 0         0 my $i = shift;
193 0         0 $val = $data->[$i];
194             } else {
195 0         0 return;
196             }
197             }
198             elsif ($knot->{_type} eq 'SCALAR') {
199 0 0       0 if (defined $data) {
200 0         0 $val = $$data;
201             } else {
202 0         0 return;
203             }
204             }
205             else {
206 0         0 croak "Variables of type $knot->{_type} not supported";
207             }
208              
209 0 0       0 if (my $inner = _is_kid($val)) {
210 0         0 my $s = $inner->seg;
211 0         0 $inner->{_data} = $knot->_decode($s);
212             }
213 0         0 return $val;
214              
215             }
216             sub CLEAR {
217 0     0   0 my $knot = shift;
218              
219 0 0       0 if ($knot->{_type} eq 'HASH') {
    0          
220 0         0 $knot->{_data} = { };
221             }
222             elsif ($knot->{_type} eq 'ARRAY') {
223 0         0 $knot->{_data} = [ ];
224             }
225              
226             else {
227 0         0 croak "Attempt to clear non-aggegrate";
228             }
229              
230 0 0       0 if ($knot->{_lock} & LOCK_EX) {
231 0         0 $knot->{_was_changed} = 1;
232             } else {
233 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
234 0         0 croak "Could not write to shared memory: $!";
235             }
236             }
237             }
238             sub DELETE {
239 0     0   0 my $knot = shift;
240 0         0 my $key = shift;
241              
242 0 0       0 $knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock};
243 0         0 my $val = delete $knot->{_data}->{$key};
244 0 0       0 if ($knot->{_lock} & LOCK_EX) {
245 0         0 $knot->{_was_changed} = 1;
246             } else {
247 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
248 0         0 croak "Could not write to shared memory: $!";
249             }
250             }
251              
252 0         0 return $val;
253             }
254             sub EXISTS {
255 0     0   0 my $knot = shift;
256 0         0 my $key = shift;
257              
258 0 0       0 $knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock};
259 0         0 return exists $knot->{_data}->{$key};
260             }
261             sub FIRSTKEY {
262 0     0   0 my $knot = shift;
263              
264 0         0 $knot->{_iterating} = 1;
265 0 0       0 $knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock};
266 0         0 my $reset = keys %{$knot->{_data}};
  0         0  
267 0         0 my $first = each %{$knot->{_data}};
  0         0  
268 0         0 return $first;
269             }
270             sub NEXTKEY {
271 0     0   0 my $knot = shift;
272              
273             # caveat emptor if hash was changed by another process
274 0         0 my $next = each %{$knot->{_data}};
  0         0  
275 0 0       0 if (not defined $next) {
276 0         0 $knot->{_iterating} = 0;
277 0         0 return;
278             } else {
279 0         0 $knot->{_iterating} = 1;
280 0         0 return $next;
281             }
282             }
283       0     sub EXTEND {
284             #XXX Noop
285             }
286             sub PUSH {
287 0     0   0 my $knot = shift;
288              
289 0 0       0 if (! exists $global_register{$knot->seg->id}) {
290 0         0 $global_register{$knot->seg->id} = $knot;
291             }
292              
293 0 0       0 $knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock};
294              
295 0         0 push @{$knot->{_data}}, @_;
  0         0  
296 0 0       0 if ($knot->{_lock} & LOCK_EX) {
297 0         0 $knot->{_was_changed} = 1;
298             } else {
299 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
300 0         0 croak "Could not write to shared memory: $!";
301             };
302             }
303             }
304             sub POP {
305 0     0   0 my $knot = shift;
306              
307 0 0       0 $knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock};
308              
309 0         0 my $val = pop @{$knot->{_data}};
  0         0  
310 0 0       0 if ($knot->{_lock} & LOCK_EX) {
311 0         0 $knot->{_was_changed} = 1;
312             } else {
313 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
314 0         0 croak "Could not write to shared memory: $!";
315             }
316             }
317 0         0 return $val;
318             }
319             sub SHIFT {
320 0     0   0 my $knot = shift;
321              
322 0 0       0 $knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock};
323 0         0 my $val = shift @{$knot->{_data}};
  0         0  
324 0 0       0 if ($knot->{_lock} & LOCK_EX) {
325 0         0 $knot->{_was_changed} = 1;
326             } else {
327 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
328 0         0 croak "Could not write to shared memory: $!";
329             }
330             }
331 0         0 return $val;
332             }
333             sub UNSHIFT {
334 0     0   0 my $knot = shift;
335              
336 0 0       0 $knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock};
337 0         0 my $val = unshift @{$knot->{_data}}, @_;
  0         0  
338 0 0       0 if ($knot->{_lock} & LOCK_EX) {
339 0         0 $knot->{_was_changed} = 1;
340             } else {
341 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
342 0         0 croak "Could not write to shared memory: $!";
343             }
344             }
345 0         0 return $val;
346             }
347             sub SPLICE {
348 0     0   0 my($knot, $off, $n, @av) = @_;
349              
350 0 0       0 $knot->{_data} = $knot->_decode($knot->seg, $knot->{_data}) unless $knot->{_lock};
351 0         0 my @val = splice @{$knot->{_data}}, $off, $n, @av;
  0         0  
352 0 0       0 if ($knot->{_lock} & LOCK_EX) {
353 0         0 $knot->{_was_changed} = 1;
354             } else {
355 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
356 0         0 croak "Could not write to shared memory: $!";
357             }
358             }
359 0         0 return @val;
360             }
361             sub FETCHSIZE {
362 0     0   0 my $knot = shift;
363              
364 0 0       0 $knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock};
365 0         0 return scalar(@{$knot->{_data}});
  0         0  
366             }
367             sub STORESIZE {
368 0     0   0 my $knot = shift;
369 0         0 my $n = shift;
370              
371 0 0       0 $knot->{_data} = $knot->_decode($knot->seg) unless $knot->{_lock};
372 0         0 $#{$knot->{_data}} = $n - 1;
  0         0  
373 0 0       0 if ($knot->{_lock} & LOCK_EX) {
374 0         0 $knot->{_was_changed} = 1;
375             } else {
376 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
377 0         0 croak "Could not write to shared memory: $!";
378             }
379             }
380 0         0 return $n;
381             }
382              
383             # --- Public methods
384              
385             sub new {
386 0     0 1 0 my ($class, %opts) = @_;
387              
388 0   0     0 my $type = $opts{var} || 'HASH';
389              
390 0 0       0 if ($type eq 'HASH') {
391 0         0 my $k = tie my %h, 'IPC::Shareable', \%opts;
392 0         0 return \%h;
393             }
394 0 0       0 if ($type eq 'ARRAY') {
395 0         0 my $k = tie my @a, 'IPC::Shareable', \%opts;
396 0         0 return \@a;
397             }
398 0 0       0 if ($type eq 'SCALAR') {
399 0         0 my $k = tie my $s, 'IPC::Shareable', \%opts;
400 0         0 return \$s;
401             }
402             }
403             sub global_register {
404             # This is a ridiculous way to do this, but if we don't call Dumper, hashes
405             # that are created in a separate process than the parent hash don't
406             # show up properly in the global register. t/90
407              
408             local $SIG{__WARN__} = sub {
409 0     0   0 my ($warning) = @_;
410 0 0       0 if ($warning !~ /hash after insertion/) {
411 0         0 warn $warning;
412             }
413 0     0 1 0 };
414              
415 0         0 Dumper \%global_register;
416              
417 0         0 return \%global_register;
418             }
419             sub process_register {
420 0     0 1 0 return \%process_register;
421             }
422              
423             sub attributes {
424 19     19 1 41 my ($knot, $attr) = @_;
425              
426 19         34 my $attrs = $knot->{attributes};
427              
428 19 50       28 if (defined $attr) {
429 19         68 return $knot->{attributes}{$attr};
430             }
431             else {
432 0         0 return $knot->{attributes};
433             }
434             }
435             sub ipcs {
436 1     1 1 4911 my $count = `ipcs -m | wc -l`;
437 1         23 chomp $count;
438 1         47 return int($count);
439             }
440             sub spawn {
441 0     0 1 0 my ($knot, %opts) = @_;
442              
443 0 0       0 croak "spawn() requires a key/glue sent in..." if ! defined $opts{key};
444              
445 0 0       0 $opts{mode} = 0666 if ! defined $opts{mode};
446              
447 0         0 $SIG{CHLD} = 'IGNORE';
448              
449             _spawn(
450             key => $opts{key},
451             mode => $opts{mode},
452 0         0 );
453             }
454             sub _spawn {
455 0     0   0 my (%opts) = @_;
456              
457 0         0 my $pid = fork;
458 0 0       0 return if $pid;
459              
460 0 0       0 if (! $pid) {
461             tie my %h, 'IPC::Shareable', {
462             key => $opts{key},
463             create => 1,
464             #exclusive => 1,
465             destroy => $opts{destroy},
466             mode => $opts{mode},
467 0         0 };
468              
469 0         0 $h{__ipc}->{run} = 1;
470              
471 0         0 while (1) {
472 0     0   0 local $SIG{__WARN__} = sub {};
473 0 0       0 last if ! defined $h{__ipc};
474 0 0       0 last if ! $h{__ipc}->{run};
475             }
476              
477 0 0       0 IPC::Shareable->clean_up_all if $opts{destroy};
478 0         0 exit 0;
479             }
480             }
481             sub unspawn {
482 0     0 1 0 shift;
483 0         0 my ($key, $destroy) = @_;
484              
485 0   0     0 $destroy ||= 0;
486              
487 0         0 tie my %h, 'IPC::Shareable', {
488             key => $key,
489             destroy => $destroy,
490             mode => 0666,
491             };
492              
493 0         0 $h{__ipc}->{run} = 0;
494              
495 0         0 $SIG{CHLD} = undef;
496              
497 0         0 sleep 1;
498              
499 0 0       0 IPC::Shareable->clean_up_all if $destroy;
500             }
501             sub lock {
502 0     0 1 0 my ($knot, $flags) = @_;
503 0 0       0 $flags = LOCK_EX if ! defined $flags;
504              
505 0 0       0 return $knot->unlock if ($flags & LOCK_UN);
506              
507 0 0       0 return 1 if ($knot->{_lock} & $flags);
508              
509             # If they have a different lock than they want, release it first
510 0 0       0 $knot->unlock if ($knot->{_lock});
511              
512 0         0 my $sem = $knot->sem;
513 0         0 my $return_val = $sem->op(@{ $semop_args{$flags} });
  0         0  
514 0 0       0 if ($return_val) {
515 0         0 $knot->{_lock} = $flags;
516 0         0 $knot->{_data} = $knot->_decode($knot->seg),
517             }
518 0         0 return $return_val;
519             }
520             sub unlock {
521 2     2 1 3 my $knot = shift;
522              
523 2 50       6 return 1 unless $knot->{_lock};
524 0 0       0 if ($knot->{_was_changed}) {
525 0 0       0 if (! defined $knot->_encode($knot->seg, $knot->{_data})){
526 0         0 croak "Could not write to shared memory: $!\n";
527             }
528 0         0 $knot->{_was_changed} = 0;
529             }
530 0         0 my $sem = $knot->sem;
531 0         0 my $flags = $knot->{_lock} | LOCK_UN;
532 0 0       0 $flags ^= LOCK_NB if ($flags & LOCK_NB);
533 0         0 $sem->op(@{ $semop_args{$flags} });
  0         0  
534              
535 0         0 $knot->{_lock} = 0;
536              
537 0         0 1;
538             }
539             *shlock = \&lock;
540             *shunlock = \&unlock;
541              
542             sub clean_up {
543 0     0 1 0 my $class = shift;
544              
545 0         0 for my $id (keys %process_register) {
546 0         0 my $s = $process_register{$id};
547 0 0       0 next unless $s->attributes('owner') == $$;
548 0 0       0 next if $s->attributes('protected');
549 0         0 remove($s);
550             }
551             }
552             sub clean_up_all {
553 0     0 1 0 my $class = shift;
554              
555 0         0 my $global_register = __PACKAGE__->global_register;
556              
557 0         0 my %deleted = %$global_register;
558              
559 0         0 for my $id (keys %deleted) {
560 0         0 my $s = $deleted{$id};
561 0 0       0 next if $s->attributes('protected');
562 0         0 remove($s);
563             }
564             }
565             sub clean_up_protected {
566 0     0 1 0 my ($knot, $protect_key);
567              
568 0 0       0 if (scalar @_ == 2) {
569 0         0 ($knot, $protect_key) = @_;
570             }
571 0 0       0 if (scalar @_ == 1) {
572 0         0 ($protect_key) = @_;
573             }
574              
575 0 0       0 if (! defined $protect_key) {
576 0         0 croak "clean_up_protected() requires a \$protect_key param";
577             }
578              
579 0 0       0 if ($protect_key !~ /^\d+$/) {
580 0         0 croak
581             "clean_up_protected() \$protect_key must be an integer. You sent $protect_key";
582             }
583              
584 0         0 for my $s (values %global_register) {
585 0         0 my $stored_key = $s->attributes('protected');
586              
587 0 0 0     0 if ($stored_key && $stored_key == $protect_key) {
588 0         0 remove($s);
589             }
590             }
591             }
592             sub remove {
593 1     1 1 2 my $knot = shift;
594              
595 1         2 my $s = $knot->seg;
596 1         3 my $id = $s->id;
597              
598 1 50       2 $s->remove or warn "Couldn't remove shared memory segment $id: $!";
599              
600 1         7 $s = $knot->sem;
601              
602 1 50       7 $s->remove or warn "Couldn't remove semaphore set $id: $!";
603              
604 1         22 delete $process_register{$id};
605 1         81 delete $global_register{$id};
606             }
607             sub seg {
608 7     7 1 11 my ($knot) = @_;
609 7 50       25 return $knot->{_shm} if defined $knot->{_shm};
610             }
611             sub sem {
612 1     1 1 2 my ($knot) = @_;
613 1 50       10 return $knot->{_sem} if defined $knot->{_sem};
614             }
615             sub singleton {
616 0     0 1 0 my ($class, $glue, $warn) = @_;
617              
618 0 0       0 if (! defined $glue) {
619 0         0 croak "singleton() requires a GLUE parameter";
620             }
621              
622 0 0       0 $warn = 0 if ! defined $warn;
623              
624 0         0 tie my $lock, 'IPC::Shareable', {
625             key => $glue,
626             create => 1,
627             exclusive => 1,
628             graceful => 1,
629             destroy => 1,
630             warn => $warn
631             };
632              
633 0         0 return $$;
634             }
635              
636             END {
637 37     37   22986 _end();
638             }
639              
640             # --- Private methods below
641              
642             sub _encode {
643 0     0   0 my ($knot, $seg, $data) = @_;
644              
645 0         0 my $serializer = $knot->attributes('serializer');
646              
647 0 0       0 if ($serializer eq 'storable') {
    0          
648 0         0 return _freeze($seg, $data);
649             }
650             elsif ($serializer eq 'json'){
651 0         0 return _encode_json($seg, $data);
652             }
653              
654 0         0 return undef;
655             }
656             sub _end {
657 37     37   48 for my $s (values %process_register) {
658 2         5 unlock($s);
659 2 50       4 next if $s->attributes('protected');
660 2 100       4 next if ! $s->attributes('destroy');
661 1 50       2 next if $s->attributes('owner') != $$;
662 1         2 remove($s);
663             }
664             }
665             sub _decode {
666 0     0   0 my ($knot, $seg) = @_;
667              
668 0         0 my $serializer = $knot->attributes('serializer');
669              
670 0 0       0 if ($serializer eq 'storable') {
    0          
671 0         0 return _thaw($seg);
672             }
673             elsif ($serializer eq 'json'){
674 0         0 return _decode_json($seg);
675             }
676              
677 0         0 return undef;
678             }
679             sub _encode_json {
680 0     0   0 my $seg = shift;
681 0         0 my $data = shift;
682              
683 0         0 my $json = encode_json $data;
684              
685 0 0       0 if (length($json) > $seg->size) {
686 0         0 croak "Length of shared data exceeds shared segment size";
687             }
688 0         0 $seg->shmwrite($json);
689             }
690             sub _decode_json {
691 0     0   0 my $seg = shift;
692              
693 0         0 my $json = $seg->shmread;
694              
695 0 0       0 return if ! $json;
696              
697             # Remove \x{0} after end of string (broke JSON)
698              
699 0         0 $json =~ s/\x00+//;
700              
701             # my $tag = substr $json, 0, 14, '';
702              
703             # if ($tag eq 'IPC::Shareable') {
704 0         0 my $data = decode_json $json;
705 0 0       0 if (! defined($data)){
706 0         0 croak "Munged shared memory segment (size exceeded?)";
707             }
708 0         0 return $data;
709             # } else {
710             # return;
711             # }
712             }
713             sub _freeze {
714 0     0   0 my $seg = shift;
715 0         0 my $water = shift;
716              
717 0         0 my $ice = freeze $water;
718             # Could be a large string. No need to copy it. substr more efficient
719 0         0 substr $ice, 0, 0, 'IPC::Shareable';
720              
721 0 0       0 if (length($ice) > $seg->size) {
722 0         0 croak "Length of shared data exceeds shared segment size";
723             }
724 0         0 $seg->shmwrite($ice);
725             }
726             sub _thaw {
727 2     2   2 my $seg = shift;
728              
729 2         14 my $ice = $seg->shmread;
730              
731 2 50       9 return if ! $ice;
732              
733 2         6 my $tag = substr $ice, 0, 14, '';
734              
735 2 50       5 if ($tag eq 'IPC::Shareable') {
736 0         0 my $water = thaw $ice;
737 0 0       0 if (! defined($water)){
738 0         0 croak "Munged shared memory segment (size exceeded?)";
739             }
740 0         0 return $water;
741             } else {
742 2         31 return;
743             }
744             }
745             sub _tie {
746 2     2   10 my ($type, $class, $key_str, $opts);
747              
748 2 50       13 if (scalar @_ == 4) {
749 0         0 ($type, $class, $key_str, $opts) = @_;
750 0         0 $opts->{key} = $key_str;
751             }
752             else {
753 2         5 ($type, $class, $opts) = @_;
754             }
755              
756 2         9 $opts = _parse_args($opts);
757              
758 2         10 my $knot = bless { attributes => $opts }, $class;
759              
760 2         9 my $key = $knot->_shm_key;
761 2         11 my $flags = $knot->_shm_flags;
762 2         3 my $shm_size = $knot->attributes('size');
763              
764 2 50 33     6 if ($knot->attributes('limit') && $shm_size > SHMMAX_BYTES) {
765 0         0 croak
766             "Shared memory segment size '$shm_size' is larger than max size of " .
767             SHMMAX_BYTES;
768             }
769              
770 2         4 my $seg;
771              
772 2 50       4 if ($knot->attributes('graceful')) {
773 0         0 my $exclusive = eval {
774 0         0 $seg = IPC::Shareable::SharedMem->new($key, $shm_size, $flags);
775 0         0 1;
776             };
777              
778 0 0       0 if (! defined $exclusive) {
779 0 0       0 if ($knot->attributes('warn')) {
780 0         0 warn "Process ID $$ exited due to exclusive shared memory collision\n";
781             }
782 0         0 exit(0);
783             }
784             }
785             else {
786 2         18 $seg = IPC::Shareable::SharedMem->new($key, $shm_size, $flags);
787             }
788              
789 2 50       6 if (! defined $seg) {
790 0 0       0 if ($! =~ /Cannot allocate memory/) {
791 0         0 croak "\nERROR: Could not create shared memory segment: $!\n\n" .
792             "Are you using too large a size?";
793             }
794              
795 0 0       0 if ($! =~ /No space left on device/) {
796 0         0 croak "\nERROR: Could not create shared memory segment: $!\n\n" .
797             "Are you spawning too many segments in a loop?";
798             }
799              
800 0 0 0     0 if (! $knot->attributes('create')) {
    0          
801 0         0 confess "ERROR: Could not acquire shared memory segment... 'create' ".
802             "option is not set, and the segment hasn't been created " .
803             "yet:\n\n $!";
804             }
805             elsif ($knot->attributes('create') && $knot->attributes('exclusive')){
806 0         0 croak "ERROR: Could not create shared memory segment. 'create' " .
807             "and 'exclusive' are set. Does the segment already exist? " .
808             "\n\n$!";
809             }
810             else {
811 0         0 croak "ERROR: Could not create shared memory segment.\n\n$!";
812             }
813             }
814              
815 2         19 my $sem = IPC::Semaphore->new($key, 3, $flags);
816 2 50       51 if (! defined $sem){
817 0         0 croak "Could not create semaphore set: $!\n";
818             }
819              
820 2 50       4 if (! $sem->op(@{ $semop_args{(LOCK_SH)} }) ) {
  2         16  
821 0         0 croak "Could not obtain semaphore set lock: $!\n";
822             }
823              
824 2         85 %$knot = (
825             %$knot,
826             _iterating => 0,
827             _key => $key,
828             _lock => 0,
829             _shm => $seg,
830             _sem => $sem,
831             _type => $type,
832             _was_changed => 0,
833             );
834              
835 2         6 $knot->{_data} = _thaw($seg);
836              
837 2 50       10 if ($sem->getval(SEM_MARKER) != SHM_EXISTS) {
838              
839 2 50       119 if (! exists $global_register{$knot->seg->id}) {
840 2         4 $global_register{$knot->seg->id} = $knot;
841             }
842              
843 2   33     6 $process_register{$knot->seg->id} ||= $knot;
844 2 50       12 if (! $sem->setval(SEM_MARKER, SHM_EXISTS)){
845 0         0 croak "Couldn't set semaphore during object creation: $!";
846             }
847             }
848              
849 2         54 $sem->op(@{ $semop_args{(LOCK_SH|LOCK_UN)} });
  2         7  
850              
851 2         35 return $knot;
852             }
853             sub _parse_args {
854 2     2   4 my ($opts) = @_;
855              
856 2 100       34 $opts = defined $opts ? $opts : { %default_options };
857              
858 2         12 for my $k (keys %default_options) {
859 24 100       49 if (not defined $opts->{$k}) {
    50          
860 10         19 $opts->{$k} = $default_options{$k};
861             }
862             elsif ($opts->{$k} eq 'no') {
863 0 0       0 if ($^W) {
864 0         0 require Carp;
865 0         0 Carp::carp("Use of `no' in IPC::Shareable args is obsolete");
866             }
867              
868 0         0 $opts->{$k} = 0;
869             }
870             }
871 2   33     19 $opts->{owner} = ($opts->{owner} or $$);
872 2   50     13 $opts->{magic} = ($opts->{magic} or 0);
873 2         4 return $opts;
874             }
875             sub _shm_key {
876             # Generates a 32-bit CRC on the key string. The $key_str parameter is used
877             # for testing only, for purposes of testing various key strings
878              
879 2     2   5 my ($knot, $key_str) = @_;
880              
881 2   50     16 $key_str //= ($knot->attributes('key') || '');
      33        
882              
883 2         3 my $key;
884              
885 2 50       6 if ($key_str eq '') {
    0          
886 2         9 $key = IPC_PRIVATE;
887             }
888             elsif ($key_str =~ /^\d+$/) {
889 0         0 $key = $key_str;
890             }
891             else {
892 0         0 $key = crc32($key_str);
893             }
894              
895 2         14 $used_ids{$key}++;
896              
897 2 50       5 if ($key > MAX_KEY_INT_SIZE) {
898 0         0 $key = $key - MAX_KEY_INT_SIZE;
899              
900 0 0       0 if ($key == 0) {
901 0         0 croak "We've calculated a key which equals 0. This is a fatal error";
902             }
903             }
904              
905 2         10 return $key;
906             }
907             sub _shm_key_rand {
908 0     0   0 my $key;
909              
910             # Unfortunatly, the only way I know how to check if a segment exists is
911             # to actually create it. We must do that here, then remove it just to
912             # ensure the slot is available
913              
914 0         0 my $verified_exclusive = 0;
915              
916 0         0 my $check_count = 0;
917              
918 0   0     0 while (! $verified_exclusive && $check_count < EXCLUSIVE_CHECK_LIMIT) {
919 0         0 $check_count++;
920              
921 0         0 $key = _shm_key_rand_int();
922              
923 0 0       0 next if $used_ids{$key};
924              
925 0         0 my $flags;
926 0         0 $flags |= IPC_CREAT;
927 0         0 $flags |= IPC_EXCL;
928              
929 0         0 my $seg;
930              
931 0         0 my $shm_slot_available = eval {
932 0         0 $seg = IPC::Shareable::SharedMem->new($key, 1, $flags);
933 0         0 1;
934             };
935              
936 0 0       0 if ($shm_slot_available) {
937 0         0 $verified_exclusive = 1;
938 0 0       0 $seg->remove if $seg;
939             }
940             }
941              
942 0 0       0 if (! $verified_exclusive) {
943 0         0 croak
944             "_shm_key_rand() can't get an available key after $check_count tries";
945             }
946              
947 0         0 $used_ids{$key}++;
948              
949 0         0 return $key;
950             }
951             sub _shm_key_rand_int {
952 0     0   0 srand();
953 0         0 return int(rand(1_000_000));
954             }
955             sub _shm_flags {
956             # --- Parses the anonymous hash passed to constructors; returns a list
957             # --- of args suitable for passing to shmget
958 2     2   3 my ($knot) = @_;
959              
960 2         6 my $flags = 0;
961              
962 2 100       7 $flags |= IPC_CREAT if $knot->attributes('create');
963 2 50       59 $flags |= IPC_EXCL if $knot->attributes('exclusive');;
964 2   50     5 $flags |= ($knot->attributes('mode') or 0666);
965              
966 2         4 return $flags;
967             }
968             sub _mg_tie {
969 0     0     my ($parent, $val, $identifier) = @_;
970              
971 0           my $key;
972              
973 0 0         if ($parent->{_key} == IPC_PRIVATE) {
974 0           $key = IPC_PRIVATE;
975             }
976             else {
977 0           $key = _shm_key_rand();
978             }
979              
980             my %opts = (
981 0           %{ $parent->attributes },
  0            
982             key => $key,
983             exclusive => 1,
984             create => 1,
985             magic => 1,
986             );
987              
988             # XXX I wish I didn't have to take a copy of data here and copy it back in
989             # XXX Also, have to peek inside potential objects to see their implementation
990 0           my $child;
991 0   0       my $type = Scalar::Util::reftype($val) || '';
992              
993 0 0         if ($type eq "HASH") {
    0          
    0          
994 0           my %copy = %$val;
995 0           $child = tie %$val, 'IPC::Shareable', $key, { %opts };
996 0 0         croak "Could not create inner tie" if ! $child;
997              
998 0 0         _reset_segment($parent, $identifier) if $opts{tidy};
999              
1000 0           %$val = %copy;
1001             }
1002             elsif ($type eq "ARRAY") {
1003 0           my @copy = @$val;
1004 0           $child = tie @$val, 'IPC::Shareable', $key, { %opts };
1005 0 0         croak "Could not create inner tie" if ! $child;
1006              
1007 0 0         _reset_segment($parent, $identifier) if $opts{tidy};
1008              
1009 0           @$val = @copy;
1010             }
1011             elsif ($type eq "SCALAR") {
1012 0           my $copy = $$val;
1013 0           $child = tie $$val, 'IPC::Shareable', $key, { %opts };
1014 0 0         croak "Could not create inner tie" if ! $child;
1015              
1016 0           $$val = $copy;
1017             }
1018             else {
1019 0           croak "Variables of type $type not implemented";
1020             }
1021              
1022 0           return $child;
1023             }
1024             sub _is_kid {
1025 0 0   0     my $data = shift or return;
1026              
1027 0           my $type = Scalar::Util::reftype( $data );
1028 0 0         return unless $type;
1029              
1030 0           my $obj;
1031              
1032 0 0         if ($type eq "HASH") {
    0          
    0          
1033 0           $obj = tied %$data;
1034             }
1035             elsif ($type eq "ARRAY") {
1036 0           $obj = tied @$data;
1037             }
1038             elsif ($type eq "SCALAR") {
1039 0           $obj = tied $$data;
1040             }
1041              
1042 0 0         if (ref $obj eq 'IPC::Shareable') {
1043 0           return $obj;
1044             }
1045              
1046 0           return;
1047             }
1048             sub _need_tie {
1049 0     0     my ($knot, $val, $identifier) = @_;
1050              
1051 0           my $type = Scalar::Util::reftype($val);
1052 0 0         return 0 if ! $type;
1053              
1054 0           my $need_tie;
1055              
1056 0 0         if ($type eq "HASH") {
    0          
    0          
1057 0           $need_tie = !(tied %$val);
1058             }
1059             elsif ($type eq "ARRAY") {
1060 0           $need_tie = !(tied @$val);
1061             }
1062             elsif ($type eq "SCALAR") {
1063 0           $need_tie = !(tied $$val);
1064             }
1065              
1066 0 0         return $need_tie ? 1 : 0;
1067             }
1068             sub _reset_segment {
1069 0     0     my ($parent, $id) = @_;
1070              
1071 0   0       my $parent_type = Scalar::Util::reftype($parent->{_data}) || '';
1072              
1073 0 0         if ($parent_type eq 'HASH') {
    0          
1074 0           my $data = $parent->{_data};
1075 0 0 0       if (exists $data->{$id} && keys %{ $data->{$id} } && tied %{ $data->{$id} }) {
  0   0        
  0            
1076 0           (tied %{ $parent->{_data}{$id} })->remove;
  0            
1077             }
1078             }
1079             elsif ($parent_type eq 'ARRAY') {
1080 0           my $data = $parent->{_data};
1081 0 0 0       if (exists $data->[$id] && tied @{ $data->[$id] }) {
  0            
1082 0           (tied @{ $parent->{_data}[$id] })->remove;
  0            
1083             }
1084             }
1085             }
1086              
1087             sub _trace {
1088 0     0     require Carp;
1089 0           require Data::Dumper;
1090 0           my $caller = ' ' . (caller(1))[3] . " called with:\n";
1091 0           my $i = -1;
1092             my @msg = map {
1093 0           ++$i;
  0            
1094 0           my $obj;
1095 0 0         if (ref eq 'IPC::Shareable') {
1096 0           ' ' . "\$_[$i] = $_: shmid: $_->{_shm}->{_id}; " .
1097             Data::Dumper->Dump([ $_->attributes ], [ 'opts' ]);
1098             } else {
1099 0           ' ' . Data::Dumper->Dump( [ $_ ] => [ "\_[$i]" ]);
1100             }
1101             } @_;
1102 0           Carp::carp "IPC::Shareable ($$) debug:\n", $caller, @msg;
1103             }
1104             sub _debug {
1105 0     0     require Carp;
1106 0           require Data::Dumper;
1107 0           local $Data::Dumper::Terse = 1;
1108 0           my $caller = ' ' . (caller(1))[3] . " tells us that:\n";
1109             my @msg = map {
1110 0           my $obj;
  0            
1111 0 0         if (ref eq 'IPC::Shareable') {
1112 0           ' ' . "$_: shmid: $_->{_shm}->{_id}; " .
1113             Data::Dumper->Dump([ $_->attributes ], [ 'opts' ]);
1114             }
1115             else {
1116 0           ' ' . Data::Dumper::Dumper($_);
1117             }
1118             } @_;
1119 0           Carp::carp "IPC::Shareable ($$) debug:\n", $caller, @msg;
1120             }
1121       0     sub _placeholder {}
1122              
1123             1;
1124              
1125             __END__