File Coverage

blib/lib/MCE/Shared.pm
Criterion Covered Total %
statement 167 258 64.7
branch 84 192 43.7
condition 36 101 35.6
subroutine 23 27 85.1
pod 2 2 100.0
total 312 580 53.7


line stmt bran cond sub pod time code
1             ###############################################################################
2             ## ----------------------------------------------------------------------------
3             ## MCE extension for sharing data supporting threads and processes.
4             ##
5             ###############################################################################
6              
7             package MCE::Shared;
8              
9 46     46   3342621 use strict;
  46         236  
  46         1341  
10 46     46   295 use warnings;
  46         112  
  46         1408  
11              
12 46     46   1140 use 5.010001;
  46         164  
13              
14 46     46   298 no warnings qw( threads recursion uninitialized once );
  46         92  
  46         2787  
15              
16             our $VERSION = '1.881';
17              
18             ## no critic (BuiltinFunctions::ProhibitStringyEval)
19             ## no critic (Subroutines::ProhibitSubroutinePrototypes)
20             ## no critic (TestingAndDebugging::ProhibitNoStrict)
21              
22 46     46   305 use Carp ();
  46         160  
  46         1617  
23              
24             $Carp::Internal{ (__PACKAGE__) }++;
25              
26 46     46   256 no overloading;
  46         85  
  46         1843  
27              
28 46     46   2569 use MCE::Mutex ();
  46         2511  
  46         998  
29 46     46   27614 use MCE::Shared::Server ();
  46         156  
  46         1437  
30 46     46   280 use Scalar::Util qw( blessed );
  46         95  
  46         3490  
31              
32             our @CARP_NOT = qw(
33             MCE::Shared::Array MCE::Shared::Hash MCE::Shared::Queue
34             MCE::Shared::Cache MCE::Shared::Minidb MCE::Shared::Scalar
35             MCE::Shared::Condvar MCE::Shared::Object MCE::Shared::Sequence
36             MCE::Shared::Handle MCE::Shared::Ordhash MCE::Shared::Server
37             );
38              
39             sub import {
40 46     46   312 no strict 'refs'; no warnings 'redefine';
  46     46   106  
  46         1178  
  46         231  
  46         74  
  46         55794  
41 34     34   4423 *{ caller().'::mce_open' } = \&open;
  34         251  
42              
43 34         423 return;
44             }
45              
46             my $_share_deeply = 0;
47              
48             ###############################################################################
49             ## ----------------------------------------------------------------------------
50             ## Share function.
51             ##
52             ###############################################################################
53              
54             sub share {
55 160 100 66 160 1 1417 shift if (defined $_[0] && $_[0] eq 'MCE::Shared');
56              
57             # construction via module option
58 160 100 100     1422 if ( ref $_[0] eq 'HASH' && $_[0]->{module} ) {
59 123         289 my $_params = shift;
60 123         264 my $_class = $_params->{module};
61              
62 123 50       476 return MCE::Shared->condvar(@_) if ( $_class eq 'MCE::Shared::Condvar' );
63 123 50       323 return MCE::Shared->handle(@_) if ( $_class eq 'MCE::Shared::Handle' );
64 123 50       385 return MCE::Shared->queue(@_) if ( $_class eq 'MCE::Shared::Queue' );
65              
66 123         656 $_params->{class} = ':construct_module:';
67              
68             my $_obj = MCE::Shared::Server::_new(
69 123   50     1413 $_params, [ @_, delete $_params->{new} || 'new' ]
70             );
71              
72             $_obj->[6] = MCE::Mutex->new( impl => 'Channel' ) unless (
73             caller->isa('MCE::Hobo::_hash') || exists( $_params->{_DEEPLY_} )
74 123 100 100     3730 );
75              
76 123         33583 return $_obj;
77             }
78              
79 37 100 66     286 my $_params = ref $_[0] eq 'HASH' && ref $_[1] ? shift : {};
80 37         215 my $_class = blessed($_[0]);
81 37         76 my $_obj;
82              
83             # class construction failed: e.g. share( class->new(...) )
84 37 0 33     352 return '' if @_ && !$_[0] && $!;
      33        
85              
86 37 100       131 $_share_deeply = 1 if $_params->{_DEEPLY_};
87              
88             # blessed object, \@array, \%hash, or \$scalar
89 37 100 0     132 if ( $_class ) {
    50          
    0          
    0          
    0          
90 35 50       227 _incr_count($_[0]), return $_[0] if $_[0]->can('SHARED_ID');
91 35         116 $_params->{'class'} = $_class;
92              
93 35         122 $_obj = MCE::Shared::Server::_new($_params, $_[0]);
94              
95             $_obj->[6] = MCE::Mutex->new( impl => 'Channel' )
96 35 50       658 unless ( exists $_params->{_DEEPLY_} );
97             }
98             elsif ( ref $_[0] eq 'ARRAY' ) {
99 2 50 33     4 if ( tied(@{ $_[0] }) && tied(@{ $_[0] })->can('SHARED_ID') ) {
  2         15  
  0         0  
100 0         0 _incr_count(tied(@{ $_[0] })), return tied(@{ $_[0] });
  0         0  
  0         0  
101             }
102 2         23 $_obj = MCE::Shared->array($_params, @{ $_[0] });
  2         133  
103 2         14 @{ $_[0] } = (); tie @{ $_[0] }, 'MCE::Shared::Object', $_obj;
  2         14  
  2         8  
  2         61  
104             }
105             elsif ( ref $_[0] eq 'HASH' ) {
106 0 0 0     0 if ( tied(%{ $_[0] }) && tied(%{ $_[0] })->can('SHARED_ID') ) {
  0         0  
  0         0  
107 0         0 _incr_count(tied(%{ $_[0] })), return tied(%{ $_[0] });
  0         0  
  0         0  
108             }
109 0         0 $_obj = MCE::Shared->hash($_params, %{ $_[0] });
  0         0  
110 0         0 %{ $_[0] } = (); tie %{ $_[0] }, 'MCE::Shared::Object', $_obj;
  0         0  
  0         0  
  0         0  
111             }
112 0         0 elsif ( ref $_[0] eq 'SCALAR' && !ref ${ $_[0] } ) {
113 0 0 0     0 if ( tied(${ $_[0] }) && tied(${ $_[0] })->can('SHARED_ID') ) {
  0         0  
  0         0  
114 0         0 _incr_count(tied(${ $_[0] })), return tied(${ $_[0] });
  0         0  
  0         0  
115             }
116 0         0 $_obj = MCE::Shared->scalar($_params, ${ $_[0] });
  0         0  
117 0         0 undef ${ $_[0] }; tie ${ $_[0] }, 'MCE::Shared::Object', $_obj;
  0         0  
  0         0  
  0         0  
118             }
119              
120             # synopsis
121             elsif ( ref $_[0] eq 'REF' ) {
122 0         0 _croak('A "REF" type is not supported');
123             }
124             else {
125 0 0       0 if ( ref $_[0] eq 'GLOB' ) {
126 0         0 _incr_count(tied(*{ $_[0] })), return $_[0] if (
127 0 0 0     0 tied(*{ $_[0] }) && tied(*{ $_[0] })->can('SHARED_ID')
  0         0  
  0         0  
128             );
129             }
130 0         0 _croak('Synopsis: blessed object, \@array, \%hash, or \$scalar');
131             }
132              
133 37         15115 return $_obj;
134             }
135              
136             ###############################################################################
137             ## ----------------------------------------------------------------------------
138             ## Public functions.
139             ##
140             ###############################################################################
141              
142             our $AUTOLOAD; # MCE::Shared::
143              
144             sub AUTOLOAD {
145 232     232   17375804 my $_fcn = $AUTOLOAD; substr($_fcn, 0, rindex($_fcn,':') + 1, '');
  232         1861  
146              
147 232 100 66     2675 shift if ( defined $_[0] && $_[0] eq 'MCE::Shared' );
148              
149 232 100       2354 return MCE::Shared::Object::_init(@_) if $_fcn eq 'init';
150 201 100       1070 return MCE::Shared::Server::_start() if $_fcn eq 'start';
151 138 50       375 return MCE::Shared::Server::_stop() if $_fcn eq 'stop';
152 138 50       330 return MCE::Shared::Server::_pid() if $_fcn eq 'pid';
153              
154 138 100 100     1391 if ( $_fcn eq 'array' || $_fcn eq 'hash' ) {
    100 33        
    50          
155 16         189 _use( 'MCE::Shared::'.ucfirst($_fcn) );
156 16 100       155 my $_params = ref $_[0] eq 'HASH' ? shift : {};
157              
158 16 100       215 $_params->{module} = ( $_fcn eq 'array' )
159             ? 'MCE::Shared::Array' : 'MCE::Shared::Hash';
160              
161 16         91 my $_obj = &share($_params);
162 16         116 delete $_params->{module};
163              
164 16 50       215 if ( scalar @_ ) {
165 16 100       54 if ( $_share_deeply ) {
166 9         97 $_params->{_DEEPLY_} = 1;
167 9 100       92 if ( $_fcn eq 'array' ) {
168 6         61 for ( my $i = 0; $i <= $#_; $i += 1 ) {
169 16 50       76 &_share($_params, $_obj, $_[$i]) if ref($_[$i]);
170             }
171             } else {
172 3         24 for ( my $i = 1; $i <= $#_; $i += 2 ) {
173 9 50       69 &_share($_params, $_obj, $_[$i]) if ref($_[$i]);
174             }
175             }
176             }
177 16         507 $_obj->assign(@_);
178             }
179              
180 16         60 $_share_deeply = 0;
181              
182 16         310 return $_obj;
183             }
184             elsif ( $_fcn eq 'handle' ) {
185 1 50       5 require MCE::Shared::Handle unless $INC{'MCE/Shared/Handle.pm'};
186              
187 1         10 my $_obj = &share( MCE::Shared::Handle->new([]) );
188 46     46   370 my $_fh = \do { no warnings 'once'; local *FH };
  46         692  
  46         26139  
  1         19  
  1         33  
189              
190 1         6 tie *{ $_fh }, 'MCE::Shared::Object', $_obj;
  1         44  
191 1 50       11 if ( @_ ) { $_obj->OPEN(@_) or return ''; }
  1 50       29  
192              
193 1         18 return $_fh;
194             }
195             elsif ( $_fcn eq 'pdl' || $_fcn =~ /^pdl_(s?byte|u?short|u?long|indx|u?longlong|float|l?double|sequence|zeroe?s|ones|g?random)$/ ) {
196              
197 0 0       0 $_fcn = $1 if ( $_fcn ne 'pdl' );
198 0 0       0 push @_, $_fcn; _use('PDL') or _croak($@);
  0         0  
199              
200 0         0 my $_obj = MCE::Shared::Server::_new(
201             { 'class' => ':construct_pdl:' }, [ @_ ]
202             );
203              
204 0         0 $_obj->[6] = MCE::Mutex->new( impl => 'Channel' );
205              
206 0         0 return $_obj;
207             }
208              
209             # cache, condvar, minidb, ordhash, queue, scalar, sequence, et cetera
210 121 50       326 $_fcn = 'sequence' if $_fcn eq 'num_sequence';
211 121         530 my $_pkg = ucfirst( lc $_fcn ); local $@;
  121         226  
212              
213 121 50 66 14   2614 if ( $INC{"MCE/Shared/$_pkg.pm"} || eval "use MCE::Shared::$_pkg (); 1" ) {
  14         19778  
  14         46  
  14         174  
214 121         370 $_pkg = "MCE::Shared::$_pkg";
215              
216 121 100       1133 return &share({}, $_pkg->new(@_)) if ( $_fcn =~ /^(?:condvar|queue)$/ );
217 89         458 return &share({ module => $_pkg }, @_);
218             }
219              
220 0         0 _croak("Can't locate object method \"$_fcn\" via package \"MCE::Shared\"");
221             }
222              
223             sub open (@) {
224 8 50 66 8 1 17424 shift if ( defined $_[0] && $_[0] eq 'MCE::Shared' );
225 8 50       28 require MCE::Shared::Handle unless $INC{'MCE/Shared/Handle.pm'};
226              
227 8         12 my $_obj;
228 8 100 66     50 if ( ref $_[0] eq 'GLOB' && tied *{ $_[0] } &&
  6 50 66     57  
229 6         27 ref tied(*{ $_[0] }) eq 'MCE::Shared::Object' ) {
230              
231 6         10 $_obj = tied *{ $_[0] };
  6         20  
232             }
233             elsif ( @_ ) {
234 2 50 33     8 if ( ref $_[0] eq 'GLOB' && tied *{ $_[0] } ) {
  0         0  
235 0 0       0 close $_[0] if defined ( fileno $_[0] );
236             }
237 2         35 $_obj = &share( MCE::Shared::Handle->new([]) );
238 46     46   366 $_[0] = \do { no warnings 'once'; local *FH };
  46         87  
  46         78106  
  2         30  
  2         54  
239 2         7 tie *{ $_[0] }, 'MCE::Shared::Object', $_obj;
  2         43  
240             }
241              
242 8 50       21 shift; _croak("Not enough arguments for open") unless @_;
  8         27  
243              
244 8 100       22 if ( !defined wantarray ) {
245 1 50       5 $_obj->OPEN(@_) or _croak("open error: $!");
246             } else {
247 7         43 $_obj->OPEN(@_);
248             }
249             }
250              
251             ###############################################################################
252             ## ----------------------------------------------------------------------------
253             ## TIE support.
254             ##
255             ###############################################################################
256              
257             sub TIEARRAY {
258 4     4   440 shift; $_share_deeply = 1;
  4         8  
259              
260 4 50 33     76 ( ref($_[0]) eq 'HASH' && exists $_[0]->{'module'} )
261             ? _tie('TIEARRAY', @_) : MCE::Shared->array(@_);
262             }
263              
264             sub TIEHANDLE {
265 0 0   0   0 shift; require MCE::Shared::Handle unless $INC{'MCE/Shared/Handle.pm'};
  0         0  
266              
267             # Tie *FH, 'MCE::Shared', { module => 'MCE::Shared::Handle' }, '>>', \*STDOUT
268             # doesn't work on the Windows platform. We'd let OPEN handle the ref instead.
269              
270 0 0 0     0 shift if ref($_[0]) eq 'HASH' && $_[0]->{'module'} eq 'MCE::Shared::Handle';
271              
272 0 0 0     0 if ( ref($_[0]) eq 'HASH' && exists $_[0]->{'module'} ) {
273 0 0 0     0 if ( @_ == 3 && ref $_[2] && defined( my $_fd = fileno($_[2]) ) ) {
      0        
274 0         0 _tie('TIEHANDLE', $_[0], $_[1]."&=$_fd");
275             } else {
276 0         0 _tie('TIEHANDLE', @_);
277             }
278             }
279             else {
280 0         0 my $_obj = &share( MCE::Shared::Handle->new([]) );
281 0 0       0 if ( @_ ) { $_obj->OPEN(@_) or return ''; }
  0 0       0  
282              
283 0         0 $_obj;
284             }
285             }
286              
287             sub TIEHASH {
288 9     9   1152 shift; $_share_deeply = 1;
  9         21  
289              
290             return _tie('TIEHASH', @_) if (
291 9 50 66     108 ref($_[0]) eq 'HASH' && exists $_[0]->{'module'}
292             );
293              
294 3         12 my ($_cache, $_ordered);
295              
296 3 50       15 if ( ref $_[0] eq 'HASH' ) {
297 0 0 0     0 if ( $_[0]->{'ordered'} || $_[0]->{'ordhash'} ) {
    0 0        
298 0         0 $_ordered = 1; shift;
  0         0  
299             } elsif ( exists $_[0]->{'max_age'} || exists $_[0]->{'max_keys'} ) {
300 0         0 $_cache = 1;
301             }
302             }
303             else {
304 3 50 0     21 if ( @_ < 3 && ( $_[0] eq 'ordered' || $_[0] eq 'ordhash' ) ) {
    50 33        
      0        
      33        
305 0         0 $_ordered = $_[1]; splice(@_, 0, 2);
  0         0  
306             } elsif ( @_ < 5 && ( $_[0] eq 'max_age' || $_[0] eq 'max_keys' ) ) {
307 0         0 $_cache = 1;
308             }
309             }
310              
311 3 50       9 return MCE::Shared->cache(@_) if $_cache;
312 3 50       12 return MCE::Shared->ordhash(@_) if $_ordered;
313 3         30 return MCE::Shared->hash(@_);
314             }
315              
316             sub TIESCALAR {
317 73     73   1056 shift;
318              
319 73 50 33     1465 ( ref($_[0]) eq 'HASH' && exists $_[0]->{'module'} )
320             ? _tie('TIESCALAR', @_) : MCE::Shared->scalar(@_);
321             }
322              
323             ###############################################################################
324             ## ----------------------------------------------------------------------------
325             ## Private functions.
326             ##
327             ###############################################################################
328              
329             sub _croak {
330 0 0   0   0 if ( $INC{'MCE.pm'} ) {
331 0         0 goto &MCE::_croak;
332             } else {
333 0 0       0 require MCE::Shared::Base unless $INC{'MCE/Shared/Base.pm'};
334 0         0 goto &MCE::Shared::Base::_croak;
335             }
336             }
337              
338             sub _incr_count {
339             # increments counter for safety during destroy
340 0     0   0 MCE::Shared::Server::_incr_count($_[0]->SHARED_ID);
341             }
342              
343             sub _share {
344 0     0   0 $_[2] = &share($_[0], $_[2]);
345              
346 0         0 MCE::Shared::Object::_req2(
347             'M~DEE', $_[1]->SHARED_ID()."\n", $_[2]->SHARED_ID()."\n"
348             );
349             }
350              
351             sub _tie {
352 6     6   21 my ( $_fcn, $_params ) = ( shift, shift );
353              
354 6 50       24 _use( my $_module = $_params->{'module'} ) or _croak("$@\n");
355              
356 6 50       309 _croak("Can't locate object method \"$_fcn\" via package \"$_module\"")
357             unless eval qq{ $_module->can('$_fcn') };
358              
359 6         27 $_params->{class} = ':construct_module:';
360 6         18 $_params->{tied } = 1;
361              
362 6         12 my $_obj;
363              
364 6 50       69 if ( $_params->{'module'}->isa('MCE::Shared::Array') ) {
    50          
365 0         0 $_obj = MCE::Shared::Server::_new($_params, [ (), $_fcn ]);
366 0 0       0 if ( @_ ) {
367 0         0 $_params->{_DEEPLY_} = 1; delete $_params->{module};
  0         0  
368 0         0 for ( my $i = 0; $i <= $#_; $i += 1 ) {
369 0 0       0 &_share($_params, $_obj, $_[$i]) if ref($_[$i]);
370             }
371 0         0 $_obj->assign(@_);
372             }
373             }
374             elsif ( $_params->{'module'}->isa('MCE::Shared::Hash') ) {
375 0         0 $_obj = MCE::Shared::Server::_new($_params, [ (), $_fcn ]);
376 0 0       0 if ( @_ ) {
377 0         0 $_params->{_DEEPLY_} = 1; delete $_params->{module};
  0         0  
378 0         0 for ( my $i = 1; $i <= $#_; $i += 2 ) {
379 0 0       0 &_share($_params, $_obj, $_[$i]) if ref($_[$i]);
380             }
381 0         0 $_obj->assign(@_);
382             }
383             }
384             else {
385 6         42 $_obj = MCE::Shared::Server::_new($_params, [ @_, $_fcn ]);
386             }
387              
388 6 50 33     336 if ( $_obj && $_obj->[2] ) {
389             ##
390             # Set encoder/decoder automatically for supported DB modules.
391             # - AnyDBM_File, DB_File, GDBM_File, NDBM_File, ODBM_File, SDBM_File,
392             # - CDB_File, SQLite_File, Tie::Array::DBD, Tie::Hash::DBD,
393             # - BerkeleyDB::*, KyotoCabinet::DB, TokyoCabinet::*
394             ##
395 0         0 $_obj->[2] = MCE::Shared::Server::_get_freeze(),
396             $_obj->[3] = MCE::Shared::Server::_get_thaw();
397             }
398              
399 6         195 $_obj->[6] = MCE::Mutex->new( impl => 'Channel' );
400              
401 6         5205 $_share_deeply = 0;
402              
403 6         270 return $_obj;
404             }
405              
406             sub _use {
407 22     22   100 my $_class = $_[0];
408              
409 22 50       89 return 1 if $_class eq 'main';
410              
411 22 50       287 if ( $_class =~ /(.*)::_/ ) {
    50          
    50          
    50          
    50          
    50          
412             # e.g. MCE::Hobo::_hash
413 0 0       0 eval "require $1" unless $INC{ join('/',split(/::/,$1)).'.pm' };
414             }
415             elsif ( $_class =~ /^(BerkeleyDB)::(?:Btree|Hash|Queue|Recno)$/ ) {
416 0 0       0 eval "require $1" unless $INC{"$1.pm"};
417             }
418             elsif ( $_class =~ /^(TokyoCabinet|KyotoCabinet)::[ABH]?DB$/ ) {
419 0 0       0 eval "require $1" unless $INC{"$1.pm"};
420             }
421             elsif ( $_class =~ /^Tie::(?:Std|Extra)Hash$/ ) {
422 0 0       0 eval "require Tie::Hash" unless $INC{'Tie/Hash.pm'};
423             }
424             elsif ( $_class eq 'Tie::StdArray' ) {
425 0 0       0 eval "require Tie::Array" unless $INC{'Tie/Array.pm'};
426             }
427             elsif ( $_class eq 'Tie::StdScalar' ) {
428 0 0       0 eval "require Tie::Scalar" unless $INC{'Tie/Scalar.pm'};
429             }
430              
431 22 100       2795 return 1 if eval q{
432             $_class->can('new') ||
433             $_class->can('TIEARRAY') || $_class->can('TIEHANDLE') ||
434             $_class->can('TIEHASH') || $_class->can('TIESCALAR')
435             };
436              
437 1 50       15 if ( !exists $INC{ join('/',split(/::/,$_class)).'.pm' } ) {
438             # remove tainted'ness from $_class
439 1         20 ($_class) = $_class =~ /(.*)/;
440              
441 1 50       143 eval "use $_class (); 1" or return '';
442             }
443              
444 1         3 return 1;
445             }
446              
447             1;
448              
449             __END__