File Coverage

blib/lib/PDL/Parallel/threads.pm
Criterion Covered Total %
statement 75 92 81.5
branch 16 30 53.3
condition 2 6 33.3
subroutine 11 11 100.0
pod 3 5 60.0
total 107 144 74.3


line stmt bran cond sub pod time code
1             package PDL::Parallel::threads;
2              
3 3     3   1752 use strict;
  3         7  
  3         137  
4 3     3   15 use warnings;
  3         5  
  3         195  
5 3     3   18 use Carp;
  3         4  
  3         216  
6 3     3   29 use PDL::LiteF;
  3         5  
  3         16  
7 3     3   18 use PDL::Exporter;
  3         6  
  3         13  
8             our $VERSION = '0.07';
9              
10             our @ISA = ( 'PDL::Exporter' );
11             our @EXPORT_OK = qw(share_pdls retrieve_pdls free_pdls);
12             our %EXPORT_TAGS = (Func=>\@EXPORT_OK);
13              
14             my $can_use_threads;
15             BEGIN {
16 3     3   10 $can_use_threads = eval {
17 3         2430 require threads;
18 0         0 threads->import();
19 0         0 require threads::shared;
20 0         0 threads::shared->import();
21 0         0 1;
22             };
23             }
24              
25             # These are the means by which we share data across Perl threads. Note that
26             # we cannot share ndarrays directly across threads, but we can share arrays
27             # of scalars, scalars whose integer values are the pointers to ndarray data,
28             # etc.
29             my %datasv_pointers :shared;
30             my %dataref_svs;
31             my %dim_arrays :shared;
32             my %types :shared;
33             my %badflag :shared;
34             my %badvalue :shared;
35             my %nbytes :shared;
36             my %originating_tid :shared;
37              
38             # PDL data should not be naively copied by Perl. Tell PDL we know about this
39             $PDL::no_clone_skip_warning = 1;
40              
41             sub auto_package_name {
42 18     18 0 37 my $name = shift;
43 18         95 my ($package_name) = caller(1);
44 18 100       131 $name = "$package_name/$name" if $name =~ /^\w+$/;
45 18         54 return $name;
46             }
47              
48             sub share_pdls {
49 4 50   4 1 31 croak("share_pdls: expected key/value pairs")
50             unless @_ % 2 == 0;
51 4         15 my %to_store = @_;
52              
53 4         19 while (my ($name, $to_store) = each %to_store) {
54 4         11 $name = auto_package_name($name);
55              
56             # Make sure we're not overwriting already shared data
57 4 50       16 if (exists $datasv_pointers{$name}) {
58 0         0 croak("share_pdls: you already have data associated with '$name'");
59             }
60              
61 4 50       9 if ( eval{$to_store->isa("PDL")} ) {
  4         34  
62             # Integers (which can be cast to and from
63             # pointers) are easily shared using threads::shared
64             # in a shared hash. This method provides a
65             # way to obtain the pointer to the datasv for
66             # the incoming ndarray, and it increments the
67             # SV's refcount.
68 4         8 $dataref_svs{$name} = eval{
69 4 50       37 croak("the ndarray does not have any allocated memory\n")
70             if !$to_store->allocated;
71 4         23 $to_store->get_dataref;
72             };
73 4 50       13 if ($@) {
74 0         0 my $error = $@;
75 0         0 chomp $error;
76 0         0 delete $datasv_pointers{$name};
77 0         0 croak('share_pdls: Could not share an ndarray under '
78             . "name '$name' because $error");
79             }
80 4         13 $datasv_pointers{$name} = 0+$dataref_svs{$name};
81 4         49 $to_store->set_donttouchdata($nbytes{$name} = $to_store->nbytes); # protect its memory
82 4 50       13 if ($can_use_threads) {
83 0         0 $dim_arrays{$name} = shared_clone([$to_store->dims]);
84 0         0 $originating_tid{$name} = threads->tid;
85             }
86             else {
87 4         37 $dim_arrays{$name} = [$to_store->dims];
88             }
89 4         17 $types{$name} = $to_store->get_datatype;
90 4         18 $badflag{$name} = $to_store->badflag;
91 4         62 my $badval = $to_store->badvalue->sclr;
92 4 50       45 $badval = shared_clone([$badval->Re,$badval->Im]) if ref $badval ;
93 4         27 $badvalue{$name} = $badval;
94             }
95             else {
96 0         0 croak("share_pdls passed data under '$name' that it doesn't "
97             . "know how to store");
98             }
99             }
100             }
101              
102              
103              
104             # Frees the memory associated with the given names.
105             sub free_pdls {
106             # Keep track of each name that is successfully freed
107 2     2 1 939 my @removed;
108              
109 2         7 for my $short_name (@_) {
110 2         8 my $name = auto_package_name($short_name);
111              
112             # If it's a regular ndarray, decrement the memory's refcount
113 2 50       11 if (exists $datasv_pointers{$name}) {
114 2         7 delete $dataref_svs{$name};
115 2         5 delete $datasv_pointers{$name};
116 2         7 delete $dim_arrays{$name};
117 2         5 delete $types{$name};
118 2         4 delete $badflag{$name};
119 2         5 delete $badvalue{$name};
120 2         6 delete $nbytes{$name};
121 2         5 delete $originating_tid{$name};
122 2         6 push @removed, $name;
123             }
124             # If its none of the above, indicate that we didn't free anything
125             else {
126 0         0 push @removed, '';
127             }
128             }
129              
130 2         8 return @removed;
131             }
132              
133             # PDL method to share an individual ndarray
134             sub PDL::share_as {
135 4     4 0 2243 my ($self, $name) = @_;
136 4         16 share_pdls(auto_package_name($name) => $self);
137 4         11 return $self;
138             }
139              
140             # Method to get an ndarray that points to the shared data associated with the
141             # given name(s).
142             sub retrieve_pdls {
143 8 50   8 1 4709 return if @_ == 0;
144              
145 8         17 my @to_return;
146 8         18 for my $short_name (@_) {
147 8         22 my $name = auto_package_name($short_name);
148              
149 8 100       28 if (exists $datasv_pointers{$name}) {
150             # Make sure that the originating thread still exists, or the
151             # data will be gone.
152 7 0 33     30 if ($can_use_threads and $originating_tid{$name} > 0
      33        
153             and not defined (threads->object($originating_tid{$name}))
154             ) {
155 0         0 croak("retrieve_pdls: '$name' was created in a thread that "
156             . "has ended or is detached");
157             }
158              
159             # Create the new thinly wrapped ndarray
160 7         51 my $new_ndarray = PDL->new_around_datasv($datasv_pointers{$name});
161 7         30 $new_ndarray->set_datatype($types{$name});
162 7         30 $new_ndarray->badflag($badflag{$name});
163             $new_ndarray->badvalue(ref $badvalue{$name}
164 0         0 ? Math::Complex->make(@{$badvalue{$name}})
165 7 50       79 : $badvalue{$name});
166 7         36 $new_ndarray->setdims(\@{$dim_arrays{$name}});
  7         76  
167 7         28 $new_ndarray->set_donttouchdata($nbytes{$name}); # protect its memory
168 7         23 push @to_return, $new_ndarray;
169             }
170             else {
171 1         16 croak("retrieve_pdls could not find data associated with '$name'");
172             }
173             }
174              
175             # In list context, return all the ndarrays
176 7 50       20 return @to_return if wantarray;
177              
178             # Scalar context only makes sense if they asked for a single name
179 7 50       46 return $to_return[0] if @_ == 1;
180              
181             # We're here if they asked for multiple names but assigned the result
182             # to a single scalar, which is probably not what they meant:
183 0           carp("retrieve_pdls: requested many ndarrays... in scalar context?");
184 0           return $to_return[0];
185             }
186              
187             1;
188              
189             __END__