File Coverage

blib/lib/threads/shared.pm
Criterion Covered Total %
statement 28 37 75.6
branch 1 4 25.0
condition n/a
subroutine 11 15 73.3
pod 7 7 100.0
total 47 63 74.6


line stmt bran cond sub pod time code
1             package threads::shared;
2              
3 2     2   69611 use 5.008;
  2         16  
4              
5 2     2   10 use strict;
  2         2  
  2         81  
6 2     2   13 use warnings;
  2         3  
  2         51  
7 2     2   8 use Config;
  2         4  
  2         106  
8              
9 2     2   22 use Scalar::Util qw(reftype refaddr blessed);
  2         4  
  2         472  
10              
11             our $VERSION = '1.59'; # Please update the pod, too.
12             my $XS_VERSION = $VERSION;
13             $VERSION = eval $VERSION;
14              
15             # Declare that we have been loaded
16             $threads::shared::threads_shared = 1;
17              
18             # Method of complaint about things we can't clone
19             $threads::shared::clone_warn = undef;
20              
21             # Load the XS code, if applicable
22             if ($Config::Config{'useithreads'} && $threads::threads) {
23             require XSLoader;
24             XSLoader::load('threads::shared', $XS_VERSION);
25              
26             *is_shared = \&_id;
27              
28             } else {
29             # String eval is generally evil, but we don't want these subs to
30             # exist at all if 'threads' is not loaded successfully.
31             # Vivifying them conditionally this way saves on average about 4K
32             # of memory per thread.
33 2     2 1 2768 eval <<'_MARKER_';
  2     2 1 2770  
  0     0 1 0  
  2     2 1 3350  
  0     0 1 0  
  5     5 1 8617  
34             sub share (\[$@%]) { return $_[0] }
35             sub is_shared (\[$@%]) { undef }
36             sub cond_wait (\[$@%];\[$@%]) { undef }
37             sub cond_timedwait (\[$@%]$;\[$@%]) { undef }
38             sub cond_signal (\[$@%]) { undef }
39             sub cond_broadcast (\[$@%]) { undef }
40             _MARKER_
41             }
42              
43              
44             ### Export ###
45              
46             sub import
47             {
48             # Exported subroutines
49 2     2   14038 my @EXPORT = qw(share is_shared cond_wait cond_timedwait
50             cond_signal cond_broadcast shared_clone);
51 2 50       8 if ($threads::threads) {
52 0         0 push(@EXPORT, 'bless');
53             }
54              
55             # Export subroutine names
56 2         6 my $caller = caller();
57 2         4 foreach my $sym (@EXPORT) {
58 2     2   15 no strict 'refs';
  2         4  
  2         1221  
59 14         18 *{$caller.'::'.$sym} = \&{$sym};
  14         1679  
  14         28  
60             }
61             }
62              
63              
64             # Predeclarations for internal functions
65             my ($make_shared);
66              
67              
68             ### Methods, etc. ###
69              
70             sub threads::shared::tie::SPLICE
71             {
72 0     0     require Carp;
73 0           Carp::croak('Splice not implemented for shared arrays');
74             }
75              
76              
77             # Create a thread-shared clone of a complex data structure or object
78             sub shared_clone
79             {
80 0 0   0 1   if (@_ != 1) {
81 0           require Carp;
82 0           Carp::croak('Usage: shared_clone(REF)');
83             }
84              
85 0           return $make_shared->(shift, {});
86             }
87              
88              
89             ### Internal Functions ###
90              
91             # Used by shared_clone() to recursively clone
92             # a complex data structure or object
93             $make_shared = sub {
94             my ($item, $cloned) = @_;
95              
96             # Just return the item if:
97             # 1. Not a ref;
98             # 2. Already shared; or
99             # 3. Not running 'threads'.
100             return $item if (! ref($item) || is_shared($item) || ! $threads::threads);
101              
102             # Check for previously cloned references
103             # (this takes care of circular refs as well)
104             my $addr = refaddr($item);
105             if (exists($cloned->{$addr})) {
106             # Return the already existing clone
107             return $cloned->{$addr};
108             }
109              
110             # Make copies of array, hash and scalar refs and refs of refs
111             my $copy;
112             my $ref_type = reftype($item);
113              
114             # Copy an array ref
115             if ($ref_type eq 'ARRAY') {
116             # Make empty shared array ref
117             $copy = &share([]);
118             # Add to clone checking hash
119             $cloned->{$addr} = $copy;
120             # Recursively copy and add contents
121             push(@$copy, map { $make_shared->($_, $cloned) } @$item);
122             }
123              
124             # Copy a hash ref
125             elsif ($ref_type eq 'HASH') {
126             # Make empty shared hash ref
127             $copy = &share({});
128             # Add to clone checking hash
129             $cloned->{$addr} = $copy;
130             # Recursively copy and add contents
131             foreach my $key (keys(%{$item})) {
132             $copy->{$key} = $make_shared->($item->{$key}, $cloned);
133             }
134             }
135              
136             # Copy a scalar ref
137             elsif ($ref_type eq 'SCALAR') {
138             $copy = \do{ my $scalar = $$item; };
139             share($copy);
140             # Add to clone checking hash
141             $cloned->{$addr} = $copy;
142             }
143              
144             # Copy of a ref of a ref
145             elsif ($ref_type eq 'REF') {
146             # Special handling for $x = \$x
147             if ($addr == refaddr($$item)) {
148             $copy = \$copy;
149             share($copy);
150             $cloned->{$addr} = $copy;
151             } else {
152             my $tmp;
153             $copy = \$tmp;
154             share($copy);
155             # Add to clone checking hash
156             $cloned->{$addr} = $copy;
157             # Recursively copy and add contents
158             $tmp = $make_shared->($$item, $cloned);
159             }
160              
161             } else {
162             require Carp;
163             if (! defined($threads::shared::clone_warn)) {
164             Carp::croak("Unsupported ref type: ", $ref_type);
165             } elsif ($threads::shared::clone_warn) {
166             Carp::carp("Unsupported ref type: ", $ref_type);
167             }
168             return undef;
169             }
170              
171             # If input item is an object, then bless the copy into the same class
172             if (my $class = blessed($item)) {
173             bless($copy, $class);
174             }
175              
176             # Clone READONLY flag
177             if ($ref_type eq 'SCALAR') {
178             if (Internals::SvREADONLY($$item)) {
179             Internals::SvREADONLY($$copy, 1) if ($] >= 5.008003);
180             }
181             }
182             if (Internals::SvREADONLY($item)) {
183             Internals::SvREADONLY($copy, 1) if ($] >= 5.008003);
184             }
185              
186             return $copy;
187             };
188              
189             1;
190              
191             __END__