File Coverage

blib/lib/threadsx/shared.pm
Criterion Covered Total %
statement 72 134 53.7
branch 4 40 10.0
condition 2 34 5.8
subroutine 21 28 75.0
pod n/a
total 99 236 41.9


line stmt bran cond sub pod time code
1             package
2             threadsx::shared; ## no index
3 69     69   394 use strict;
  69         122  
  69         1670  
4 69     69   291 use warnings;
  69         130  
  69         1400  
5 69     69   19404 use threads::shared;
  69         60449  
  69         365  
6              
7             our $VERSION = '0.15';
8              
9             ######################################################################
10             #
11             # useful edits and extensions to threads::shared
12             #
13             # 1. support splice on shared arrays
14             # 2. support CODE refs in shared data structures
15             # 3. support GLOB refs in shared data structures
16             #
17              
18             $threadsx::shared::shared = 0;
19             our $share_code;
20             our $share_glob;
21             our $make_shared;
22              
23             sub import {
24 69     69   7207 no warnings 'redefine';
  69         132  
  69         8336  
25 69 50   69   234 if ($threadsx::shared::shared++) {
26              
27             }
28 69 50       196 if (!defined &share_orig) {
29 69         189 *share_orig = \&threads::shared::share;
30             }
31 69         165 *threads::shared::shared_clone = \&threadsx::shared::_shared_clone;
32 69         169 *threads::shared::share = \&threadsx::shared::_share;
33 69         365 *threads::shared::tie::SPLICE = \&threadsx::shared::_tie_SPLICE;
34 69         146 $share_code = 1;
35 69         91 $share_glob = 1;
36 69         150 my $caller = caller();
37 69         135 foreach my $sym (qw(share is_shared cond_wait cond_timewait
38             cond_signal cond_broadcast shared_clone bless)) {
39 552 100 66     2138 next if $sym eq 'bless' && !$threads::threads;
40 69     69   386 no strict 'refs';
  69         120  
  69         4844  
41 483         555 *{$caller . '::' . $sym} = \&{'threads::shared::' . $sym};
  483         1405  
  483         1085  
42             }
43             }
44              
45             sub _tie_SPLICE {
46 69     69   381 use B;
  69         119  
  69         25852  
47 0     0   0 my ($tied,$off,$len,@list) = @_;
48 0         0 my @bav = B::AV::ARRAY($tied);
49 0         0 my $arraylen = 0 + @bav;
50             # ::xdiag("SPLICE \@A,$off,$len/$arraylen,\@B:" . (0+@list));
51              
52 0   0     0 $off ||= 0;
53 0 0       0 if ($off < 0) {
54 0         0 $off += $arraylen;
55 0 0       0 if ($off < 0) {
56 0         0 require Carp;
57 0         0 Carp::croak("Modification of non-createable array value "
58             . "attempted, subscript $_[1]");
59             }
60             }
61 0 0 0     0 if (!defined $len || $len eq 'undef') {
62 0         0 $len = $arraylen - $off;
63             }
64 0 0       0 if ($len < 0) {
65 0         0 $len += $arraylen - $off;
66 0 0       0 if ($len < 0) {
67 0         0 $len = 0;
68             }
69             }
70             # if ($off+$len > $arraylen) {
71             # $len = $arraylen-$off;
72             # }
73              
74 0         0 my (@tmp, @val);
75              
76 0         0 for (my $i=0; $i<$off; $i++) {
77 0         0 my $fetched = $bav[$i]->object_2svref;
78 0         0 push @tmp, $$fetched;
79             }
80 0         0 for (my $i=0; $i<$len; $i++) {
81 0 0       0 last if $i+$off > $arraylen;
82 0         0 my $fetched = $bav[$i+$off]->object_2svref;
83 0   0     0 push @val, defined($fetched) && $$fetched;
84             }
85 0         0 push @tmp, map { _shared_clone($_) } @list;
  0         0  
86 0         0 for (my $i=$off+$len; $i<$arraylen; $i++) {
87 0         0 my $fetched = $bav[$i]->object_2svref;
88 0         0 push @tmp, $$fetched;
89             }
90              
91             # is there a better way to clear the array?
92 0         0 $tied->POP for 0..$arraylen;
93 0         0 $tied->PUSH(@tmp);
94 0 0       0 return wantarray ? @val : @val ? $val[-1] : undef;
    0          
95             }
96              
97             sub _share (\[$@%]) {
98 0 0 0 0   0 if (ref($_[0]) eq 'CODE' && $share_code) {
    0 0        
    0          
99 0         0 return $_[0] = threadsx::shared::code->new( $_[0] );
100             } elsif (ref($_[0]) eq 'GLOB' && $share_glob) {
101 0         0 return $_[0] = threadsx::shared::glob->new( $_[0] );
102             } elsif (ref($_[0]) eq 'REF') {
103 69     69   450 no overloading '${}';
  69         130  
  69         12607  
104 0 0 0     0 if (ref(${$_[0]}) eq 'CODE' && $share_code) {
  0 0 0     0  
105 0         0 return $_[0] = threadsx::shared::code->new( ${$_[0]} );
  0         0  
106 0         0 } elsif (ref(${$_[0]}) eq 'GLOB' && $share_glob) {
107 0         0 return $_[0] = threadsx::shared::glob->new( ${$_[0]} );
  0         0  
108             }
109             }
110 0         0 share_orig( $_[0] );
111             }
112              
113             sub _shared_clone {
114 42     42   239 return $make_shared->(shift, {});
115             };
116              
117              
118             # copied and modified from threads::shared 1.48
119             $make_shared = sub {
120             package
121             threads::shared;
122 69     69   399 use Scalar::Util qw(reftype refaddr blessed);
  69         118  
  69         7933  
123             my ($item,$cloned) = @_;
124             return $item if (!ref($item) || threads::shared::is_shared($item)
125             || !$threads::threads);
126             my $addr = refaddr($item);
127             return $cloned->{$addr} if exists $cloned->{$addr};
128             my ($ref_type,$copy) = reftype($item);
129             if ($ref_type eq 'ARRAY') {
130             $copy = &threads::shared::share( [] );
131             $cloned->{$addr} = $copy;
132 69     69   370 no overloading '@{}';
  69         126  
  69         4645  
133             push @$copy, map { $make_shared->($_,$cloned) } @$item;
134             } elsif ($ref_type eq 'HASH') {
135 69     69   394 no overloading;
  69         147  
  69         5084  
136             my $ccc = {};
137             $copy = &threads::shared::share( $ccc );
138             $cloned->{$addr} = $copy;
139              
140             while (my ($k,$v) = each %$item) {
141             $copy->{$k} = $make_shared->($v,$cloned);
142             }
143             } elsif ($ref_type eq 'SCALAR') {
144 69     69   366 no overloading '${}';
  69         120  
  69         4155  
145             $copy = \do{ my $scalar = $$item };
146             threads::shared::share($copy);
147             $cloned->{$addr} = $copy;
148             } elsif ($ref_type eq 'REF') {
149 69     69   352 no overloading '${}';
  69         133  
  69         12031  
150             if ($addr == refaddr($$item)) {
151             $copy = \$copy;
152             threads::shared::share($copy);
153             $cloned->{$addr} = $copy;
154             } else {
155             my $tmp;
156             $copy = \$tmp;
157             threads::shared::share($copy);
158             $cloned->{$addr} = $copy;
159             $tmp = $make_shared->($$item,$cloned);
160             }
161             } elsif ($ref_type eq 'CODE') {
162             $copy = $cloned->{$addr} = threadsx::shared::code->new($item);
163             } elsif ($ref_type eq 'GLOB') {
164             $copy = $cloned->{$addr} = threadsx::shared::glob->new($item);
165             } else {
166             require Carp;
167             if (! defined $threads::shared::clone_warn) {
168             Carp::croak("Unsupported ref type: ", $ref_type);
169             } elsif ($threads::shared::clone_warn) {
170             Carp::carp("Unsupported ref type: ", $ref_type);
171             }
172             return undef;
173             }
174              
175             # If input item is an object, then bless the copy into the same class
176             if (my $class = blessed($item)) {
177             CORE::bless($copy, $class);
178             }
179              
180             # Clone READONLY flag
181             if ($ref_type eq 'SCALAR') {
182 69     69   382 no overloading '${}';
  69         126  
  69         11772  
183             if (Internals::SvREADONLY($$item)) {
184             Internals::SvREADONLY($$copy, 1) if ($] >= 5.008003);
185             }
186             }
187             if (Internals::SvREADONLY($item)) {
188             Internals::SvREADONLY($copy, 1) if ($] >= 5.008003);
189             }
190              
191             return $copy;
192             };
193              
194             package
195             threadsx::shared::code;
196 69     69   411 use overload fallback => 1, '&{}' => 'code';
  69         113  
  69         376  
197 69     69   2806 use Carp;
  69         117  
  69         10700  
198             our %CODE_LOOKUP;
199             sub new {
200 0     0     my ($pkg,$ref) = @_;
201 0 0         if (ref($ref) eq $pkg) {
    0          
202 0           carp "threadsx::shared::code: ref is already shareable code";
203 0           return $ref;
204             } elsif (ref($ref) ne 'CODE') {
205 0           croak "usage: $pkg->new(CODE)";
206             }
207 0           my $id = Scalar::Util::refaddr($ref);
208 0   0       $CODE_LOOKUP{$id} //= $ref;
209 0           threads::shared::shared_clone(CORE::bless \$id, $pkg);
210             }
211             sub code {
212 69     69   397 no overloading '${}';
  69         128  
  69         7323  
213             return $CODE_LOOKUP{${$_[0]}} ||
214 0   0 0     sub { croak "threadsx::shared::code: bad ",__PACKAGE__," id ${$_[0]}" };
  0     0      
  0            
215             }
216              
217             package
218             threadsx::shared::glob;
219 69     69   417 use overload fallback => 1, '*{}' => 'glob';
  69         145  
  69         311  
220 69     69   2535 use Carp;
  69         128  
  69         10410  
221             our %GLOB_LOOKUP;
222             sub new {
223 0     0     my ($pkg,$ref) = @_;
224 0 0         if (ref($ref) eq $pkg) {
    0          
225 0           carp "threadsx::shared::glob: ref is already shareable glob";
226 0           return $ref;
227             } elsif (ref($ref) ne 'GLOB') {
228 0           croak "usage: $pkg->new(GLOB)";
229             }
230 0           my $id = Scalar::Util::refaddr($ref);
231 0   0       $GLOB_LOOKUP{$id} //= $ref;
232 0           threads::shared::shared_clone(CORE::bless \$id, $pkg);
233             }
234             sub glob {
235 69     69   427 no overloading '${}';
  69         198  
  69         6407  
236 0   0 0     return $GLOB_LOOKUP{${$_[0]}} || *STDERR
237             }
238              
239             1;
240              
241             =head1 NAME
242              
243             threadsx::shared - useful extensions to threads::shared
244              
245             =head1 VERSION
246              
247             0.15
248              
249             =head1 DESCRIPTION
250              
251              
252              
253             =head1 NAME
254              
255             threadsx::shared - extension to C, the Perl extension
256             for sharing data structures between threads
257              
258             =head1 VERSION
259              
260             This document describes threadsx::shared version 0.15
261              
262             =head1 DESCRIPTION
263              
264             See L for the synopsis and API of the C
265             module. This module extends C to give it three new
266             capabilities:
267              
268             =over 4
269              
270             =item 1. Support the SPLICE operation on shared arrays
271              
272             =item 2. Provide a workaround to share CODE references between threads
273              
274             =item 3. Provide a workaround to share GLOB references between threads
275              
276             =back
277              
278             =head2 SPLICE operation on shared arrays
279              
280             Current versions of L do not support splice operationss
281             on arrays that have been shared.
282              
283             $ perl -Mthreads -Mthreads::shared -e \
284             'share(@a);@a=(1..10);print splice @a,3,3'
285             Splice not implemented for shared arrays at -e line 1.
286              
287             The C module works around this restriction by
288             hijacking the C method and emulating the
289             splice operation without a call to the builtin C function.
290             The performance isn't as good as a native C call, but it is
291             better than a sharp stick in the eye.
292              
293             $ perl -Mthreads -Mthreadsx::shared -e \
294             'share(@a);@a=(1..10);print splice @a,3,3'
295             456
296              
297              
298             =head2 Sharing CODE references
299              
300             Current versions of L do not support sharing of code
301             references or data structures that contain code references
302              
303             $ perl -Mthreads -Mthreads::shared -e \
304             '$dispatch=shared_clone( {bar=>sub{42}, baz=>\&CORE::warn} )'
305             Unsupported ref type: CODE at -e line 1.
306              
307             The C module employs a workaround, hijacking the method
308             used by C to identify and share references.
309             The new method substitutes each CODE reference with a shareable,
310             overloaded object that behaves like the underlying CODE reference.
311              
312             $ perl -Mthreads -Mthreadsx::shared -e \
313             '$dispatch=shared_clone( {bar=>sub{42}, baz=>\&CORE::warn} );
314             print $dispatch->{bar}->()'
315             42
316              
317             This feature requires perl v5.18 or better.
318              
319             =head2 Sharing GLOB references
320              
321             Current versions of L do not support sharing of GLOB
322             references or data structures that contain GLOB references
323              
324             $ perl -Mthreads -Mthreads::shared -e \
325             'open my $fh,">foo";$x=shared_clone({foo=>$fh})'
326             Unsupported ref type: GLOB at -e line 1.
327              
328             The C module employs a workaround, hijacking the
329             method used by C to identify and share
330             referemces. The new method substitutes each GLOB reference with a
331             shareable, overloaded object that behaves like the underlying GLOB
332             reference.
333              
334             $ perl -Mthreads -Mthreadsx::shared -e \
335             'open $fh,">foo";$x=shared_clone({foo=>$fh});
336             print {$x->{foo}} "Hello world\n";close $x->{foo};
337             print `cat foo`'
338             Hello world
339              
340             This feature requires perl 5.18 or better.
341              
342             =head1 EXPORT
343              
344             Like L,
345             the following functions are exported by this module: C,
346             C, C, C, C, C
347             and C
348              
349             Note that if this module is imported when L has not yet been loaded,
350             then these functions all become no-ops. This makes it possible to write
351             modules that will work in both threaded and non-threaded environments.
352              
353             =head1 FUNCTIONS
354              
355             See L. The features implemented in
356             C do not define any new functions.
357              
358             =head1 NOTES
359              
360             Like L, C is designed to
361             disable itself silently if threads are not
362             available. This allows you to write modules and packages that can be used
363             in both threaded and non-threaded applications.
364              
365             If you want access to threads, you must C before you
366             C. L will emit a warning if you use it after
367             L.
368              
369             =head1 WARNINGS
370              
371             The warnings emitted by C are the same as those
372             produced by L.
373              
374             =over 4
375              
376             =item cond_broadcast() called on unlocked variable
377              
378             =item cond_signal() called on unlocked variable
379              
380             See L, above.
381              
382             =back
383              
384             =head1 BUGS AND LIMITATIONS
385              
386             Treat shared CODE and GLOB references in shared data structures
387             as read-only.
388              
389             When C is used on arrays, hashes, array refs or hash refs, any data
390             they contain will be lost.
391              
392             my @arr = qw(foo bar baz);
393             share(@arr);
394             # @arr is now empty (i.e., == ());
395              
396             # Create a 'foo' object
397             my $foo = { 'data' => 99 };
398             bless($foo, 'foo');
399              
400             # Share the object
401             share($foo); # Contents are now wiped out
402             print("ERROR: \$foo is empty\n")
403             if (! exists($foo->{'data'}));
404              
405             Therefore, populate such variables B declaring them as shared. (Scalar
406             and scalar refs are not affected by this problem.)
407              
408             It is often not wise to share an object unless the class itself has been
409             written to support sharing. For example, an object's destructor may get
410             called multiple times, once for each thread's scope exit. Another danger is
411             that the contents of hash-based objects will be lost due to the above
412             mentioned limitation. See F (in the CPAN distribution of
413             this module) for how to create a class that supports object sharing.
414              
415             Destructors may not be called on objects if those objects still exist at
416             global destruction time. If the destructors must be called, make sure
417             there are no circular references and that nothing is referencing the
418             objects, before the program ends.
419              
420             =begin html
421              
422             Does not support splice on arrays.
423             Does not support explicitly changing array lengths
424             via $#array -- use push and pop instead.
425            
426              
427             =end html
428              
429             Taking references to the elements of shared arrays and hashes does not
430             autovivify the elements, and neither does slicing a shared array/hash over
431             non-existent indices/keys autovivify the elements.
432              
433             C allows you to C<< share($hashref->{key}) >> and
434             C<< share($arrayref->[idx]) >> without giving any error message. But the
435             C<< $hashref->{key} >> or C<< $arrayref->[idx] >> is B shared, causing
436             the error "lock can only be used on shared values" to occur when you attempt
437             to C<< lock($hashref->{key}) >> or C<< lock($arrayref->[idx]) >> in another
438             thread.
439              
440             Using C is unreliable for testing
441             whether or not two shared references are equivalent (e.g., when testing for
442             circular references). Use L, instead:
443              
444             use threads;
445             use threads::shared;
446             use Scalar::Util qw(refaddr);
447              
448             # If ref is shared, use threads::shared's internal ID.
449             # Otherwise, use refaddr().
450             my $addr1 = is_shared($ref1) || refaddr($ref1);
451             my $addr2 = is_shared($ref2) || refaddr($ref2);
452              
453             if ($addr1 == $addr2) {
454             # The refs are equivalent
455             }
456              
457             L does not work properly on shared references
458             embedded in shared structures. For example:
459              
460             my %foo :shared;
461             $foo{'bar'} = shared_clone({'a'=>'x', 'b'=>'y', 'c'=>'z'});
462              
463             while (my ($key, $val) = each(%{$foo{'bar'}})) {
464             ...
465             }
466              
467             Either of the following will work instead:
468              
469             my $ref = $foo{'bar'};
470             while (my ($key, $val) = each(%{$ref})) {
471             ...
472             }
473              
474             foreach my $key (keys(%{$foo{'bar'}})) {
475             my $val = $foo{'bar'}{$key};
476             ...
477             }
478              
479             This module supports dual-valued variables created using C from
480             L. However, while C<$!> acts
481             like a dualvar, it is implemented as a tied SV. To propagate its value, use
482             the follow construct, if needed:
483              
484             my $errno :shared = dualvar($!,$!);
485              
486             View existing bug reports at, and submit any new bugs, problems, patches, etc.
487             to: L
488              
489             For bugs in the underlying L distribution, use
490             L
491              
492             =head1 SEE ALSO
493              
494             L, L, L
495              
496             L and
497             L
498              
499             Perl threads mailing list:
500             L
501              
502             =head1 AUTHOR
503              
504             Additional features for C by
505             Marty O'Brien Emob@cpan.orgE.
506              
507             Original L by
508             Artur Bergman Esky AT crucially DOT netE
509              
510             CPAN version of C
511             produced by Jerry D. Hedden Ejdhedden AT cpan DOT orgE.
512              
513             =head1 LICENSE
514              
515             C and C are released under the same
516             license as Perl.
517              
518             =cut
519