File Coverage

blib/lib/threadsx/shared.pm
Criterion Covered Total %
statement 48 110 43.6
branch 4 40 10.0
condition 2 34 5.8
subroutine 13 20 65.0
pod n/a
total 67 204 32.8


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