File Coverage

blib/lib/Thread/Task/Concurrent/Util.pm
Criterion Covered Total %
statement 17 19 89.4
branch 1 2 50.0
condition n/a
subroutine 6 6 100.0
pod 1 1 100.0
total 25 28 89.2


line stmt bran cond sub pod time code
1             package Thread::Task::Concurrent::Util;
2              
3 2     2   21341 use warnings;
  2         4  
  2         59  
4 2     2   11 use strict;
  2         4  
  2         56  
5              
6 2     2   1615 use threads::shared;
  2         1168  
  2         13  
7              
8 2     2   135 use Scalar::Util qw/refaddr reftype blessed/;
  2         2  
  2         122  
9              
10 2     2   11 use base 'Exporter';
  2         3  
  2         1341  
11             our ( @EXPORT, @EXPORT_OK, %EXPORT_TAGS );
12             our $VERSION = '0.01';
13              
14             @EXPORT = qw();
15             %EXPORT_TAGS = ();
16             @EXPORT_OK = qw(unshared_clone);
17              
18             my ($make_unshared);
19              
20             # neary 1 to 1 copied from threads::shared
21             sub unshared_clone {
22 5 50   5 1 9 if ( @_ != 1 ) {
23 0         0 require Carp;
24 0         0 Carp::croak('Usage: shared_clone(REF)');
25             }
26              
27 5         13 return $make_unshared->( shift, {} );
28             }
29              
30             $make_unshared = sub {
31             my ( $item, $cloned ) = @_;
32              
33             # Just return the item if:
34             # 1. Not a ref;
35             # 2. NOT shared; or
36             # 3. Not running 'threads'.
37             return $item if ( !ref($item) || !$threads::threads );
38              
39             # Check for previously cloned references
40             # (this takes care of circular refs as well)
41             my $addr;
42             if(is_shared($item)) {
43             $addr = "s_" . is_shared($item);
44             } else {
45             $addr = "r_" . refaddr($item);
46             }
47              
48             if ( exists( $cloned->{$addr} ) ) {
49             # Return the already existing clone
50             return $cloned->{$addr};
51             }
52              
53             # Make copies of array, hash and scalar refs and refs of refs
54             my $copy;
55             my $ref_type = reftype($item);
56              
57             # Copy an array ref
58             if ( $ref_type eq 'ARRAY' ) {
59             # Make empty shared array ref
60             $copy = [];
61             # Add to clone checking hash
62             $cloned->{$addr} = $copy;
63             # Recursively copy and add contents
64             push( @$copy, map { $make_unshared->( $_, $cloned ) } @$item );
65             }
66              
67             # Copy a hash ref
68             elsif ( $ref_type eq 'HASH' ) {
69             # Make empty shared hash ref
70             $copy = {};
71             # Add to clone checking hash
72             $cloned->{$addr} = $copy;
73             # Recursively copy and add contents
74             foreach my $key ( keys( %{$item} ) ) {
75             $copy->{$key} = $make_unshared->( $item->{$key}, $cloned );
76             }
77             }
78              
79             # Copy a scalar ref
80             elsif ( $ref_type eq 'SCALAR' ) {
81             $copy = \do { my $scalar = $$item; };
82             # Add to clone checking hash
83             $cloned->{$addr} = $copy;
84             }
85              
86             # Copy of a ref of a ref
87             elsif ( $ref_type eq 'REF' ) {
88             # Special handling for $x = \$x
89             if ( $addr == is_shared($$item) ) {
90             $copy = \$copy;
91             $cloned->{$addr} = $copy;
92             } else {
93             my $tmp;
94             $copy = \$tmp;
95             # Add to clone checking hash
96             $cloned->{$addr} = $copy;
97             # Recursively copy and add contents
98             $tmp = $make_unshared->( $$item, $cloned );
99             }
100              
101             } else {
102             require Carp;
103             Carp::croak( "Unsupported ref type: ", $ref_type );
104             }
105              
106             # If input item is an object, then bless the copy into the same class
107             if ( my $class = blessed($item) ) {
108             bless( $copy, $class );
109             }
110              
111             # Clone READONLY flag
112             if ( $ref_type eq 'SCALAR' ) {
113             if ( Internals::SvREADONLY($$item) ) {
114             Internals::SvREADONLY( $$copy, 1 ) if ( $] >= 5.008003 );
115             }
116             }
117             if ( Internals::SvREADONLY($item) ) {
118             Internals::SvREADONLY( $copy, 1 ) if ( $] >= 5.008003 );
119             }
120              
121             return $copy;
122             };
123              
124             1;
125              
126             __END__