File Coverage

blib/lib/Promise/Me.pm
Criterion Covered Total %
statement 748 1471 50.8
branch 244 1012 24.1
condition 140 582 24.0
subroutine 105 149 70.4
pod 48 51 94.1
total 1285 3265 39.3


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Promise - ~/lib/Promise/Me.pm
3             ## Version v0.4.4
4             ## Copyright(c) 2022 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2021/05/28
7             ## Modified 2022/08/24
8             ## All rights reserved
9             ##
10             ## This program is free software; you can redistribute it and/or modify it
11             ## under the same terms as Perl itself.
12             ##----------------------------------------------------------------------------
13             BEGIN
14             {
15             use Config;
16 18     18   1719312 use strict;
  18         177  
  18         714  
17 18     18   77 use warnings;
  18         25  
  18         313  
18 18     18   118 use warnings::register;
  18         27  
  18         405  
19 18     18   69 use parent qw( Module::Generic );
  18         21  
  18         2149  
20 18     18   6547 use vars qw( $KIDS $DEBUG $FILTER_RE_FUNC_ARGS $FILTER_RE_SHARED_ATTRIBUTE
  18         4755  
  18         82  
21 18         1702 $RESULT_MEMORY_SIZE $SHARED_MEMORY_SIZE $SHARED $VERSION $SHARE_MEDIUM
22             $SHARE_FALLBACK $SHARE_AUTO_DESTROY $OBJECTS_REPO $EXCEPTION_CLASS $SERIALISER );
23 18     18   153162747 use curry;
  18         36  
24 18     18   9261 use Clone;
  18         5095  
  18         541  
25 18     18   113 use Errno;
  18         30  
  18         844  
26 18     18   6893 use Filter::Util::Call ();
  18         20845  
  18         834  
27 18     18   113 use Module::Generic::File::Cache v0.2.0;
  18         163  
  18         442  
28 18     18   9193 use Module::Generic::File::Mmap v0.1.1;
  18         230743934  
  18         198  
29 18     18   16812 use Module::Generic::SharedMemXS v0.1.0 qw( :all );
  18         23023898  
  18         395  
30 18     18   20601 use Nice::Try v1.3.1;
  18         36435224  
  18         337  
31 18     18   8329 use POSIX qw( WNOHANG WIFEXITED WEXITSTATUS WIFSIGNALED );
  18         234  
  18         156  
32 18     18   65750785 use PPI;
  18         61  
  18         206  
33 18     18   2620 use Scalar::Util;
  18         37  
  18         434  
34 18     18   92 use Want;
  18         36  
  18         651  
35 18     18   89 our $KIDS = {};
  18         31  
  18         8456  
36 18     18   72 our @EXPORT = qw( async await share unshare lock unlock );
37 18         65 our @EXPORT_OK = qw( share unshare lock unlock );
38 18         39 our %EXPORT_TAGS = (
39 18         108 all => [qw( share unshare lock unlock )],
40             lock => [qw( lock unlock )],
41             share => [qw( share unshare )],
42             );
43             Exporter::export_ok_tags( 'all', 'lock', 'share' );
44 18         890 our $DEBUG = 0;
45 18         52 # share( $this ):
46             # share( \$this );
47             # share ( ( \$this ), (( @that ) ), %plop );
48             # share
49             # ( ( \$this ), (( @that ) ), %plop );
50             our $FILTER_RE_FUNC_ARGS = qr{
51 18         70 (?<func>
52             \b(?:share|unshare|lock|unlock)\b
53             [[:blank:]\h\v]*
54             (?!\{)
55             )
56             (?<args>
57             (?:
58             (?:
59             [[:blank:]\h\v]
60             |
61             \(
62             )*
63             \\?[\$\@\%\*]\w+
64             (?:[[:blank:]\h\v]|\)|,)*
65             )+
66             )
67             }x;
68             # my $val : shared;
69             # our $val : shared = 'John';
70             # our( $plop, @truc ) : shared = ( '2', qw( Pierre Paul ) );
71             our $FILTER_RE_SHARED_ATTRIBUTE;
72 18         24 if( $INC{'threads.pm'} )
73 18 50       198 {
74             $FILTER_RE_SHARED_ATTRIBUTE = qr{
75 0         0 (
76             (?:my|our)
77             (
78             (?:
79             [[:blank:]\h\v]
80             |
81             \(
82             )*
83             \\?[\$\@\%\*]\w+
84             (?:[[:blank:]\h\v]|\)|,)*
85             )+
86             \:[[:blank:]\h\v]*
87             )
88             \b(?:pshared)\b
89             }x;
90             }
91             else
92             {
93             $FILTER_RE_SHARED_ATTRIBUTE = qr{
94 18         61 (
95             (?:my|our)
96             (
97             (?:
98             [[:blank:]\h\v]
99             |
100             \(
101             )*
102             \\?[\$\@\%\*]\w+
103             (?:[[:blank:]\h\v]|\)|,)*
104             )+
105             \:[[:blank:]\h\v]*
106             )
107             \b(?:pshared|shared)\b
108             }x;
109             }
110             our $SHARED_MEMORY_SIZE = ( 64 * 1024 );
111 18         31 our $RESULT_MEMORY_SIZE = ( 512 * 1024 );
112 18         23 use constant SHARED_MEMORY_BLOCK => ( 64 * 1024 );
113 18     18   124 our $SHARED = {};
  18         311  
  18         2896  
114 18         28 our $SHARE_MEDIUM = Module::Generic::SharedMemXS->supported
115 18 0       179 ? 'memory'
    50          
116             : Module::Generic::File::Mmap->has_xs
117             ? 'mmap'
118             : 'file';
119             # If shared memory block is not supported, should we fall back to cache file?
120             our $SHARE_FALLBACK = 1;
121 18         98 our $SHARE_AUTO_DESTROY = 1;
122 18         23 # A repository of objects that is used by END and DESTROY to remove the shared
123             # space only when no proces is using it, since the processes run asynchronously
124             our $OBJECTS_REPO = [];
125 18         33 our $EXCEPTION_CLASS = 'Module::Generic::Exception';
126 18         28 our $SERIALISER = 'storable';
127 18         20 our $VERSION = 'v0.4.4';
128 18         413 };
129              
130             use strict;
131 18     18   95 use warnings;
  18         25  
  18         423  
132 18     18   72  
  18         20  
  18         2719  
133             {
134             my $class = shift( @_ );
135             my $hash = {};
136 2     35   11027 for( my $i = 0; $i < scalar( @_ ); $i++ )
137 35         1672034 {
138 35         87 if( $_[$i] eq 'debug' ||
139             $_[$i] eq 'debug_code' ||
140 35 50 33     188 $_[$i] eq 'debug_file' ||
      33        
      33        
141             $_[$i] eq 'no_filter' )
142             {
143             $hash->{ $_[$i] } = $_[$i+1];
144             CORE::splice( @_, $i, 2 );
145 34         569 $i--;
146 0         0 }
147 0         0 }
148             $hash->{debug} = 0 if( !CORE::exists( $hash->{debug} ) );
149             $hash->{no_filter} = 0 if( !CORE::exists( $hash->{no_filter} ) );
150 0 50       0 $hash->{debug_code} = 0 if( !CORE::exists( $hash->{debug_code} ) );
151 35 50       168 Filter::Util::Call::filter_add( bless( $hash => ( ref( $class ) || $class ) ) );
152 35 50       145 my $caller = caller;
153 35   33     140 no strict 'refs';
154 35         376 for( qw( ARRAY HASH SCALAR ) )
155 18     18   92 {
  18         25  
  18         36140  
156 35         945 *{"${caller}\::MODIFY_${_}_ATTRIBUTES"} = sub
157             {
158 105         389 my( $pack, $ref, $attr ) = @_;
159             {
160 105     6   613 if( $attr eq 'Promise_shared' )
161             {
162 6 50       573183 my $type = lc( ref( $ref ) );
  6         63  
163             if( $type !~ /^(array|hash|scalar)$/ )
164 6         255 {
165 6 50       69 warnings::warn( "Unsupported variable type '$type': '$ref'\n" ) if( warnings::enabled() || $DEBUG );
166             return;
167 6 0 0     159 }
168 0         0 &{"${class}\::share"}( $ref );
169             }
170 0         0 }
  6         30  
171             return;
172             };
173 6         285 }
174 35         106 $class->export_to_level( 1, @_ );
175             }
176 6         84  
177             {
178             my( $self ) = @_ ;
179             my( $status, $last_line );
180             my $line = 0;
181 35     35 1 3502 my $code = '';
182 35         639 if( $self->{no_filter} )
183 35         58 {
184 35         74 Filter::Util::Call::filter_del();
185 35 50       66 $status = 1;
186             return( $status );
187 35         1193 }
188 0         0 while( $status = Filter::Util::Call::filter_read() )
189 0         0 {
190             return( $status ) if( $status < 0 );
191 0         0 $line++;
192             if( /^__(?:DATA|END)__/ )
193 35 50       593 {
194 2925         3655 $last_line = $_;
195 2925 100       2395 last;
196             }
197 2925         3963
198 35         102 s{
199             $FILTER_RE_FUNC_ARGS
200             }
201             {
202             my $func = $+{func};
203             my $args = $+{args};
204 2890         9818 # print( STDERR "Func is '$+{func}' and args are: '$+{args}'\n" );
205 17         209 $args =~ s,(?<!\\)([\$\@\%\*]\w+),\\$1,g;
206             "$func$args";
207 17         87 }gexs;
208 17         156
209 35         82 s{
210             $FILTER_RE_SHARED_ATTRIBUTE
211 17         62 }
212             {
213             "${1}Promise_shared"
214 2890         7457 }gsex;
215            
216             s#(\b(?:share|lock|unlock|unshare)\b[[:blank:]\h]*(?!{)\(?[[:blank:]\h]*)(?=[mo\$\@\%])#$1\\#gs;
217 17         90 $code .= $_;
218 2890         5363 $_ = '';
219 2890         3169 }
220             return( $line ) if( !$line );
221 2890 50       6654 unless( $status < 0 )
222 35 50       102 {
223             $code = ' ' . $code;
224 35         97 my $doc = PPI::Document->new( \$code, readonly => 1 ) || die( "Unable to parse: ", PPI::Document->errstr, "\n$code\n" );
225 35   50     291 if( $doc = $self->_parse( $doc ) )
226 35 50       603 {
227             $_ = $doc->serialize;
228 35         3445035 }
229             # Rollback
230             else
231             {
232             $_ = $code;
233 0         0 }
234             if( CORE::length( $last_line ) )
235 35 50       181653 {
236             $_ .= $last_line;
237 35         178 }
238             }
239             unless( $status <= 0 )
240 35 50       2569 {
241             while( $status = Filter::Util::Call::filter_read() )
242 35         163 {
243             return( $status ) if( $status < 0 );
244 35 50       338 $line++;
245 35         137 }
246             }
247             if( $self->{debug_file} )
248 35 50       1000 {
249             if( open( my $fh, ">$self->{debug_file}" ) )
250 35 0       229 {
251             binmode( $fh, ':utf8' );
252 0         0 print( $fh $_ );
253 0         0 close( $fh );
254 0         0 }
255             }
256             return( $line );
257 0         0 }
258              
259             {
260             my $self = shift( @_ );
261             my $name;
262 35     28 1 220953 if( @_ >= 2 && !ref( $_[0] ) && ref( $_[1] ) eq 'CODE' )
263 28         19902 {
264 28 50 33     186 $name = shift( @_ );
      33        
265             }
266 28         833 my $code = shift( @_ );
267             return( $self->error( "No code was provided to execute." ) ) if( !defined( $code ) || ref( $code ) ne 'CODE' );
268 0         0 $self->{args} = [];
269 28 50 33     135 $self->{exception_class} = $EXCEPTION_CLASS;
270 28         569 $self->{medium} = $SHARE_MEDIUM;
271 28         833 $self->{name} = $name;
272 28         209 $self->{result_shared_mem_size} = $RESULT_MEMORY_SIZE;
273 28         199 $self->{serialiser} = $SERIALISER;
274 28         170 $self->{shared_vars_mem_size} = $SHARED_MEMORY_SIZE;
275 28         203 $self->{use_async} = 0;
276 28         168 # By default, should we use file cache to store shared data or memory?
277 28         195 $self->{use_cache_file} = ( $SHARE_MEDIUM eq 'file' ? 1 : 0 );
278             $self->{use_mmap} = ( $SHARE_MEDIUM eq 'mmap' ? 1 : 0 );
279 28 100       153 $self->{_init_strict_use_sub} = 1;
280 28 50       162 $self->SUPER::init( @_ );
281 28         190 # async sub my_subroutine { }
282 28         106 if( $self->{use_async} )
283             {
284 28 50       225 # If it fails, it will trigger reject()
285             $self->{_code} = sub
286             {
287             $self->resolve( scalar( @{$self->{args}} ) ? $code->( @{$self->{args}} ) : $code->() );
288             };
289 0 0   0   0 }
  0         0  
  0         0  
290 28         3506 # Promise::Me->new(sub{ my( $resolve, $reject ) = @_; });
291             else
292             {
293             # $self->{_code} = sub
294             # {
295             # $code->(
296             # sub{ $self->resolve( @_ ) },
297             # sub{ $self->reject( @_ ) },
298             # );
299             # };
300             $self->{_code} = $code;
301             }
302 0         0 if( $self->use_cache_file )
303             {
304 28 100       203 $self->{medium} = 'file';
    50          
305             }
306 28         221 elsif( $self->use_mmap )
307             {
308             $self->{medium} = 'mmap';
309             }
310 18         2413 $self->{_handlers} = [];
311             $self->{_no_more_chaining} = 0;
312 0         0 $self->{executed} = 0;
313 28         1427 $self->{exit_bit} = '';
314 28         90 $self->{exit_signal} = '';
315 28         70 $self->{exit_status} = '';
316 28         238 $self->{has_coredump} = 0;
317 28         226 $self->{is_child} = 0;
318 28         154 $self->{pid} = $$;
319 28         123 $self->{share_auto_destroy} = 1;
320 28         115 # promise status; data space shared between child and parent through shared memory
321 28         209 $self->{shared} = {};
322             $self->{shared_key} = 'pm' . $$;
323 28         104 $self->{shared_space_destroy} = 1;
324 28         84 $self->{global} = {};
325 28         262 $self->{global_key} = 'gl' . $$;
326 28         189 # This will be set to true if the chain ends with a call to wait()
327 28         237 # Promise::Me->new(sub{})->then->catch->wait;
328             $self->{wait} = 0;
329             # Check if there are any variables to share
330 28         231 # Because this is stored in a global variable, we use the caller's package name as namespace
331             my $pack = caller(1);
332             # Resulting values from exec, or then when there are no more handler but there could be later
333 28         160 $self->{_saved_values} = [];
334             $self->{_shared_from} = $pack;
335 28         286 push( @$OBJECTS_REPO, $self );
336 28         84  
337 28         149 # unless( Want::want( 'OBJECT' ) )
338             # {
339             # $self->no_more_chaining(1);
340             # $self->exec;
341             # }
342             return( $self );
343             }
344 28         94  
345             {
346             my $self = shift( @_ );
347             my $code = shift( @_ ) || return( $self->error( "No code reference was provided to add a final handler." ) );
348             return( $self->error( "Final handler provided is not a code reference." ) ) if( ref( $code ) ne 'CODE' );
349 28     0 1 528 push( @{$self->{_handlers}}, { type => 'finally', handler => $code });
350 0   0     0 return( $self );
351 0 0       0 }
352 0         0  
  0         0  
353 0         0 {
354             my $self = shift( @_ );
355             my $code = shift( @_ ) || return( $self->error( "No code reference was provided to add a resolve handler." ) );
356             return( $self->error( "Resolve handler provided is not a code reference." ) ) if( ref( $code ) ne 'CODE' );
357             push( @{$self->{_handlers}}, { type => 'then', handler => $code });
358 0     32 1 0 return( $self );
359 32   50     115 }
360 32 50       197  
361 32         216 {
  32         59  
362 32         398 my $self = shift( @_ );
363             my $code = shift( @_ ) || return( $self->error( "No code reference was provided to add a reject handler." ) );
364             return( $self->error( "Reject handler provided is not a code reference." ) ) if( ref( $code ) ne 'CODE' );
365             push( @{$self->{_handlers}}, { type => 'catch', handler => $code });
366             return( $self );
367 32     18 1 84 }
368 18   50     134  
369 18 50       221 {
370 18         212 my $this = shift( @_ );
  18         56  
371 18         223 return( __PACKAGE__->error( __PACKAGE__, "->all must be called as a class function such as: ", __PACKAGE__, "->all()" ) ) if( ref( $this ) || $this ne 'Promise::Me' );
372             my $opts = {};
373             $opts = pop( @_ ) if( ref( $_[-1] ) eq 'HASH' );
374             $opts->{timeout} //= 0;
375             $opts->{race} //= 0;
376 18     0 1 173 my @proms = ( scalar( @_ ) == 1 && Scalar::Util::reftype( $_[0] ) eq 'ARRAY' ) ? @{$_[0]} : @_;
377 0 0 0     0 # Make sure we are being provided with our objects
378 0         0 @proms = grep{ Scalar::Util::blessed( $_ ) && $_->isa( 'Promise::Me' ) } @proms;
379 0 0       0 return( $this->new(sub
380 0   0     0 {
381 0   0     0 my( $resolve, $reject ) = @_;
382 0 0 0     0 # We make a copy that we can manipulate, remove, etc
  0         0  
383             my @promises = @proms;
384 0 0       0 my @results;
  0         0  
385             # Size the array
386             $#results = $#proms;
387 0     0   0 my $done = {};
388             my $keep_going = 1;
389 0         0 local $SIG{ALRM} = sub{ $keep_going = 0 };
390 0         0 alarm( $opts->{timeout} ) if( $opts->{timeout} =~ /^\d+$/ );
391             COLLECT: while($keep_going)
392 0         0 {
393 0         0 for( my $i = 0; $i < scalar( @promises ); $i++ )
394 0         0 {
395 0         0 next if( CORE::exists( $done->{ $i } ) );
  0         0  
396 0 0       0 my $p = $promises[$i];
397 0         0 if( $p->rejected )
398             {
399 0         0 $done->{ $i } = 0;
400             $reject->( $p->result );
401 0 0       0 last COLLECT;
402 0         0 }
403 0 0       0 elsif( $p->resolved )
    0          
404             {
405 0         0 $done->{ $i } = 1;
406 0         0 if( $opts->{race} )
407 0         0 {
408             @results = $p->result;
409             $resolve->( @results );
410             last COLLECT;
411 0         0 }
412 0 0       0 else
413             {
414 0         0 $results[$i] = $p->result;
415 0         0 CORE::splice( @promises, $i, 1 );
416 0         0 $i--;
417             }
418             }
419             }
420 0         0 last COLLECT if( !scalar( @promises ) );
421 0         0 }
422 0         0 alarm(0);
423             if( $opts->{race} )
424             {
425             scalar( @results ) > 1 ? @results : $results[0];
426 0 0       0 }
427             else
428 0         0 {
429 0 0       0 if( !$keep_going )
430             {
431 0 0       0 $reject->( Promise::Me::Exception->new( 'timeout' ) );
432             }
433             else
434             {
435 0 0       0 $resolve->( \@results );
436             }
437 0         0 }
438             }) );
439             }
440              
441 0         0  
442              
443              
444 0         0 # Called as a function. Takes promise objects as arguments, possibly with an hash
445             # reference of options at the end
446             # away( $p1, $p2 );
447 0     8 1 0 # away( $p1, $p2, { timeout => 2 });
448             {
449 8     4 1 256 my $opts = {};
450             $opts = pop( @_ ) if( ref( $_[-1] ) eq 'HASH' );
451 4     0 1 680 my @promises = @_;
452             return if( !scalar( @promises ) );
453             @promises = grep{ Scalar::Util::blessed( $_ ) && $_->isa( 'Promise::Me' ) } @promises;
454             if( !scalar( @promises ) )
455             {
456             warn( "No promise object was provided to await()!\n" ) if( warnings::enabled() );
457             return;
458             }
459 0     2 1 0 my @results;
460 2 50       143 # Pre-size the array
461 2         44 $#results = $#promises;
462 2 50       37 my $timeout = 0;
463 2 50       32 $opts->{timeout} //= 3;
  2         19  
464 4 50       140 local $SIG{ALRM} = sub
465             {
466 2 0       23 $timeout++;
467 0         0 print( STDERR __PACKAGE__, "::await: Reached timeout of $opts->{timeout} seconds.\n" ) if( $DEBUG );
468             };
469 0         0 CORE::alarm( $opts->{timeout} );
470             printf( STDERR "%s::await: %d promise(s) to process.\n", __PACKAGE__, scalar( @promises ) ) if( $DEBUG >= 4 );
471 2         29 CHECK_KIDS: while( !$timeout )
472 2         20 {
473 2   50     19 for( my $i = 0; $i <= $#promises; $i++ )
474             {
475             my $prom = $promises[$i];
476 2     0   53 my $pid = $prom->child;
477 0 0       0 my $prefix = '[' . ( $prom->is_child ? 'child' : 'parent' ) . ']';
478 2         47 # Already removed
479 0         0 if( !CORE::defined( $pid ) || !CORE::exists( $KIDS->{ $pid } ) )
480 2 50       27 {
481 2         40 splice( @promises, $i, 1 );
482             $i--;
483 2         40 next;
484             }
485 21550         61678  
486 29321         33491 my $rv = waitpid( $pid, POSIX::WNOHANG );
487 29321 50       68937 if( $rv == 0 )
488             {
489 29321 100 66     2832459 }
490             elsif( $rv > 0 )
491 29321         3107967 {
492 4         22 CORE::delete( $KIDS->{ $pid } );
493 4         19 $prom->_set_exit_values( $? );
494             if( !$prom->resolved && !$prom->rejected )
495             {
496 4         18 # exit with value > 0 meaning an error occurred
497 29317 100       178849 if( $prom->exit_status )
    50          
    0          
498             {
499             my $err = '';
500             if( $prom->exit_signal )
501             {
502 29317         124645 $err = 'Asynchronous process killed by signal.';
503 4         71 }
504 4 0 33     77 elsif( $prom->exit_status )
505             {
506             $err = 'Asynchronous process exited due to an error.';
507 4 0       58 }
508             $prom->reject( Promise::Me::Exception->new( $err ) );
509 0         0 }
510 0 0       0 else
    0          
511             {
512 0         0 $prom->resolve;
513             }
514             }
515             $results[$i] = $prom->result;
516 0         0 }
517             # Child process has already exited
518 0         0 elsif( $rv == -1 )
519             {
520             CORE::delete( $KIDS->{ $pid } );
521             next CHECK_KIDS;
522 0         0 }
523             }
524             last if( !scalar( @promises ) );
525 0         0 # Mixing alarm and sleep yield weird results, so we temporarily back it up
526             # and deactivate it
527             my $alarm = CORE::alarm(0);
528             sleep(0.5);
529             CORE::alarm( $alarm );
530 4         52 }
531 0         0 CORE::alarm(0);
532             print( STDERR __PACKAGE__, "::await: Finished awaiting for the processes\n" ) if( $DEBUG >= 4 );
533             return( scalar( @results ) > 1 ? @results : $results[0] );
534 0 100       0 }
535              
536             {
537 21550         32698 my $self = shift( @_ );
538 21548         85553 @_ = () if( scalar( @_ ) == 1 && !defined( $_[0] ) );
539 21548         1720559 if( @_ )
540             {
541 21548         140777 my $code = shift( @_ );
542 2 50       26 return( $self->error( "catch() only accepts a code references. Value provided was '$code'." ) ) if( ref( $code ) ne 'CODE' );
543 2 50       26 $self->add_reject_handler( $code );
544             }
545            
546             # Is there more chaining, or is this the end of the chain?
547             # If the latter, we then start executing our codes
548 2     18 1 79 unless( Want::want( 'OBJECT' ) )
549 18 50 33     116 {
550 18 50       565 $self->no_more_chaining(1);
551             $self->exec;
552 18         200 }
553 18 50       89 return( $self );
554 18         220 }
555              
556              
557              
558             {
559 18 50       322 my $self = shift( @_ );
560             # Block signal for fork
561 18         120 my $sigset = POSIX::SigSet->new( POSIX::SIGINT );
562 18         1194 POSIX::sigprocmask( POSIX::SIG_BLOCK, $sigset ) ||
563             return( $self->error( "Cannot block SIGINT for fork: $!" ) );
564 18         2681 select((select(STDOUT), $|=1)[0]);
565             select((select(STDERR), $|=1)[0]);
566             $self->executed(1);
567 12     29341 0 330
568             my $pid = fork();
569 29341     8 1 95260 # Parent
570             if( $pid )
571             {
572             # $self->kids->push( $pid );
573 8     28 1 190 $KIDS->{ $pid } = { me => $self };
574             $self->child( $pid );
575 28         135 POSIX::sigprocmask( POSIX::SIG_UNBLOCK, $sigset ) ||
576 28 50       511 return( $self->error( "Cannot unblock SIGINT for fork: $!" ) );
577             my $shm = $self->_set_shared_space() || return( $self->pass_error );
578 28         390 $shm->lock( LOCK_EX );
579 28         538 $shm->write( $self->{shared} );
580 28         217 $shm->unlock;
581             # If we are to wait for the child to exit, there is no CHLD signal handler
582 28         136 if( $self->{wait} )
583             {
584 28 100       143400 # Is the child still there?
    50          
585             if( kill( 0 => $pid ) || $!{EPERM} )
586             {
587 28         3097 # Blocking wait
588 20         2152 waitpid( $pid, 0 );
589 20 50       1125 $self->_set_exit_values( $? );
590             if( WIFEXITED($?) )
591 20   50     9937 {
592 20         706 # Child exited normally
593 20         305 }
594 20         16740 else
595             {
596 20 50       71671 # Child exited with non-zero
597             }
598             }
599 20 0 0     5494 else
600             {
601             # Child has already exited
602 0         0 }
603 0         0 }
604 0 0       0 else
605             {
606             # We let perl handle itself the reaping of the child process
607             local $SIG{CHLD} = 'IGNORE';
608             }
609             return( $self );
610             }
611             # Child
612             elsif( $pid == 0 )
613             {
614             $self->is_child(1);
615             $self->pid( $$ );
616             $self->_set_shared_space() || return( $self->reject( $self->error ) );
617             my $exception_class = $self->exception_class;
618            
619             try
620             {
621 0         0 # Possibly any arguments passed in the 'async sub some_routine'; or
622             # Promise::Me->new( args => [@args] );
623 20         795 local $_ = [ $self->curry::resolve, $self->curry::reject ];
624             my $args = $self->args;
625             my $code = $self->{_code};
626             my @rv = @$args ? $code->( @$args ) : $code->();
627             # The code executed, returned a promise, so we use it and call the next 'then'
628 20         865 # in the chain with it.
629 8         819 if( scalar( @rv ) &&
630 8 50       5252 Scalar::Util::blessed( $rv[0] ) &&
631 8         1753 $rv[0]->isa( 'Promise::Me' ) )
632             {
633 8 50 33     278 shift( @rv )->resolve( @rv );
  8         1067  
  8         41  
  8         66  
  8         193  
  0         0  
  8         87  
  8         98  
634 8     8   276 }
635             elsif( scalar( @rv ) &&
636             Scalar::Util::blessed( $rv[0] ) &&
637 8         75 $exception_class &&
638 8         582 $rv[0]->isa( $exception_class ) )
639 8         921 {
640 8 50       1821 $self->reject( shift( @rv ) );
641             }
642             elsif( scalar( @rv ) )
643 8 50 66     276 {
    50 66        
    50 66        
    0 66        
    0 66        
644             $self->resolve( @rv );
645             }
646             # If the callback has used the $_->[0] to resolve the promise, we pass on to then
647 6         125458 elsif( $self->resolved )
648             {
649             # $self->resolve;
650             # The user already called resolve, so we do nothing.
651             }
652             # If the callback has used the $_->[1] to reject the promise, we pass on to catch
653             elsif( $self->rejected )
654 0         0 {
655             # $self->reject;
656             # The user already called reject, so we do nothing.
657             }
658 0         0 }
659             catch( $e )
660             {
661             if( Scalar::Util::blessed( $e ) )
662             {
663             $self->reject( $e );
664             }
665             else
666             {
667             $self->reject( Promise::Me::Exception->new( $e ) );
668             }
669             }
670             exit(0);
671             }
672             else
673 8 50 50     56 {
  6 0 33     130  
  6 0       3393  
  0 0       0  
  8 0       62  
  8 0       69  
  8 0       62  
  8 0       60  
  8 0       178  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 100       0  
  0 50       0  
  0 50       0  
  0 100       0  
  0         0  
  0         0  
  0         0  
  0         0  
  8         227  
  0         0  
  0         0  
  8         124  
  6         53  
  8         126  
  8         115  
  8         40  
  8         54  
  2         22  
  2         9  
  2         14  
674 2     2   56 my $err;
675 2 50       10 if( $! == POSIX::EAGAIN() )
676             {
677 2         13 $err = "fork cannot allocate sufficient memory to copy the parent's page tables and allocate a task structure for the child.";
678             }
679             elsif( $! == POSIX::ENOMEM() )
680             {
681 0         0 $err = "fork failed to allocate the necessary kernel structures because memory is tight.";
682             }
683 18 50 50 18   16952 else
  18 0 33     36  
  18 0 0     26125  
  2 0 33     23  
  2 0 0     77  
  2 0 0     71  
  0 0 0     0  
  2 0 0     24  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  2 0       25  
  0 0       0  
  0 0       0  
  2 0       14  
  8 0       239  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  8         43  
  0         0  
  0         0  
  0         0  
  0         0  
684 8         90 {
685             $err = "Unable to fork a new process to execute promised code: $!";
686             }
687             return( $self->reject( Module::Promise::Exception->new( $err ) ) );
688 8         565 }
689 0 0       0 return( $self );
    0          
690             }
691 0         0  
692              
693              
694              
695 0         0  
696             {
697             my $self = shift( @_ );
698             my $type = shift( @_ ) ||
699 0         0 return( $self->error( "No type provided to get its next handler." ) );
700             my $h = $self->{_handlers};
701 0         0 my( $code, $pos );
702             for( my $i = 0; $i < scalar( @$h ); $i++ )
703 0         0 {
704             if( $h->[$i]->{type} eq $type )
705             {
706 0     70 0 0 $code = $h->[$i]->{handler};
707             $pos = $i;
708 70     4 1 777 last;
709             }
710 4     4 1 60 }
711             return if( !defined( $code ) );
712 4     4 1 36 splice( @$h, 0, $pos + 1 );
713             return( $code );
714             }
715              
716 4     18 1 53  
717 18   50     63  
718              
719 18         176  
720 18         125  
721 18         92  
722             {
723 18 100       158 my $self;
724             $self = shift( @_ ) if( scalar( @_ ) && Scalar::Util::blessed( $_[0] ) && $_[0]->isa( 'Promise::Me' ) );
725 16         153 my $type;
726 10         71 $type = pop( @_ ) if( !ref( $_[-1] ) );
727 10         58 my $prefix = '[' . ( $self->is_child ? 'child' : 'parent' ) . ']';
728             foreach my $ref ( @_ )
729             {
730 10 100       52 my $tied = tied( $ref );
731 18         306 if( defined( $self ) )
732 10         100 {
733             }
734             else
735 10     0 1 170 {
736             print( STDERR __PACKAGE__, "::lock: Checking if variable '$ref' is tied -> ", ( Scalar::Util::blessed( $tied ) ? 'Yes' : 'No' ), "\n" ) if( $DEBUG >= 4 );
737 0     2 1 0 }
738             if( Scalar::Util::blessed( $tied ) &&
739 2     16 1 23 $tied->isa( 'Promise::Me::Share' ) )
740             {
741 16     4 1 156 defined( $type ) ? $tied->lock( $type ) : $tied->lock;
742             }
743 4     29405 1 40 }
744             return( $self ) if( $self );
745 29405     28 1 78956 }
746              
747              
748              
749 28     0 1 299  
750 0 0 0     0 {
      0        
751 0         0 my $this = shift( @_ );
752 0 0       0 return( __PACKAGE__->error( __PACKAGE__, "->race must be called as a class function such as: ", __PACKAGE__, "->race()" ) ) if( ref( $this ) || $this ne 'Promise::Me' );
753 0 0       0 my $opts = {};
754 0         0 $opts = pop( @_ ) if( ref( $_[-1] ) eq 'HASH' );
755             $opts->{race} = 1;
756 0         0 return( $this->all( @_, $opts ) );
757 0 0       0 }
758              
759             {
760             my $self = shift( @_ );
761             my $vals = [@_];
762 0 0       0 $self->rejected(1);
    0          
763             # Maybe there is no more reject handler, like when we are at the end of the chain.
764 0 0 0     0 my $code = $self->get_next_reject_handler();
765             if( !defined( $code ) )
766             {
767 0 0       0 $self->{_saved_values} = $vals;
768             return( $self );
769             }
770 0 0       0 try
771             {
772             my @rv = $code->( @$vals );
773 0     0 1 0 # The code returned another promise
774             if( scalar( @rv ) &&
775 0     28 1 0 Scalar::Util::blessed( $rv[0] ) &&
776             $rv[0]->isa( 'Promise::Me' ) )
777 28     26 1 124 {
778             return( shift( @rv )->resolve( @rv ) );
779             }
780             # We call our next 'then' by resolving this with the arguments received
781 26     0 1 406 elsif( scalar( @rv ) )
782 0 0 0     0 {
783 0         0 return( $self->resolve( @rv ) );
784 0 0       0 }
785 0         0 # Called in void
786 0         0 else
787             {
788             return( $self );
789             }
790             }
791 0     2 1 0 catch( $e )
792 2         3909 {
793 2         22 if( Scalar::Util::blessed( $e ) )
794             {
795 2         80 return( $self->reject( $e ) );
796 2 50       263 }
797             else
798 2         17 {
799 0         0 return( $self->reject( Promise::Me::Exception->new( $e ) ) );
800             }
801 0 50 33     0 }
  2         12  
  2         269  
  2         11  
  2         14  
  0         0  
  2         5  
  2         11  
802 2     2   32 }
803 2         9  
804              
805 2 50 33     33 {
    50 33        
806             my $self = shift( @_ );
807             my $vals = [@_];
808             my $prefix = '[' . ( $self->is_child ? 'child' : 'parent' ) . ']';
809 2         7117 if( $self->debug >= 3 )
810             {
811             my $trace = $self->_get_stack_trace;
812             }
813             # Maybe there is no more resolve handler, like when we are at the end of the chain.
814 0         0 my $code = $self->get_next_resolve_handler();
815             {
816             no warnings;
817             }
818             # # No more resolve handler. We are at the end of the chain. Mark this as resolved
819 2         22 # No actually, mark this resolved right now, and if next iteration is a fail,
820             # then it will be marked differently
821             $self->resolved(1);
822 2 0 0     8 if( !defined( $code ) || !ref( $code ) )
  0 0 33     0  
  0 0       0  
  0 0       0  
  2 0       77  
  2 0       6  
  2 0       5  
  2 0       4  
  2 0       16  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         90  
  2         15  
  0         0  
  0         0  
  0         0  
  2         24  
  2         24  
  2         12  
  2         7  
  0         0  
  0         0  
  0         0  
823 0     0   0 {
824 0 0       0 $self->{_saved_values} = $vals;
825             return( $self );
826 0         0 }
827            
828             try
829             {
830 0         0 my @rv = $code->( @$vals );
831             $self->result( @rv ) || return( $self->reject( Promise::Me::Exception->new( $self->error ) ) );
832 18 0 0 18   129 # The code returned another promise
  18 0 0     28  
  18 0 33     3206  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  2 0       73  
  2 50       15  
  2 50       27  
  0 50       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         195  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
833             if( scalar( @rv ) &&
834             Scalar::Util::blessed( $rv[0] ) &&
835 0     2 1 0 $rv[0]->isa( 'Promise::Me' ) )
836             {
837             return( shift( @rv )->resolve( @rv ) );
838             }
839 2     16 1 46 # We call our next 'then' by resolving this with the arguments received
840 16         112 elsif( scalar( @rv ) )
841 16 50       81 {
842 16 50       187 return( $self->resolve( @rv ) );
843             }
844 16         2275 # Called in void
845             else
846             {
847 0         0 return( $self );
848             }
849 18     18   103 }
  18         36  
  18         14077  
  16         73  
850             catch( $e )
851             {
852             my $ex;
853             if( Scalar::Util::blessed( $e ) )
854 16         514 {
  0         0  
855 16 100 66     184 $ex = $e;
856             }
857 16         1873 else
858 8         52 {
859             $ex = Promise::Me::Exception->new( $e );
860             }
861 8 50 33     97 $self->result( $ex );
  8         35  
  8         33  
  8         35  
  8         59  
  0         0  
  8         36  
  8         44  
862 8     8   143 return( $self->reject( $ex ) );
863 8         35 }
864 8 50       84 }
865              
866 8 50 66     9997  
    50 66        
867             {
868             my $self = shift( @_ );
869             my $shm = $self->shared_mem;
870 8         222 my $prefix = '[' . ( $self->is_child ? 'child' : 'parent' ) . ']';
871             if( @_ )
872             {
873             # We need to save the result provided as a 1 reference variable
874             my $val = ( @_ == 1 && ref( $_[0] ) ) ? shift( @_ ) : [@_];
875 0         0 if( $shm )
876             {
877             my $hash = $shm->read;
878             $hash = {} if( ref( $hash ) ne 'HASH' );
879             $hash->{result} = $val;
880 8         170 $shm->lock( LOCK_EX );
881             $shm->write( $hash ) || return( $self->pass_error( $shm->error ) );
882             $shm->unlock;
883 8 0 0     51 return( $hash );
  0 0 33     0  
  0 0       0  
  0 0       0  
  8 0       47  
  8 0       20  
  8 0       48  
  8 0       35  
  8 0       67  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  0         0  
  8         49  
  8         47  
  0         0  
  0         0  
  0         0  
  8         49  
  8         46  
  8         162  
  8         57  
  0         0  
  0         0  
  0         0  
884 0     0   0 }
885 0         0 else
886 0 0       0 {
887             # $self->message_colour( 4, "${prefix} <red>Shared memory object not found.</>" );
888 0         0 warnings::warn( "Shared space object not set or lost!\n" ) if( warnings::enabled() || $self->debug );
889             }
890             }
891             else
892 0         0 {
893             my $hash = $shm->read;
894 0         0 $hash = {} if( ref( $hash ) ne 'HASH' );
895 0         0 $self->{shared} = $hash;
896 18 0 0 18   117 return( $hash->{result} );
  18 0 0     29  
  18 0 33     20992  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  8 0       279  
  8 50       159  
  8 50       58  
  0 50       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  8         201  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
897             }
898             }
899 0     20 1 0  
900              
901              
902             # We merely register the variables the user wants to share
903 20     12 1 239 # Next time we will fork, we will share those registered variables
904 12         73 {
905 12 100       74 my $self;
906 12 100       301 $self = shift( @_ ) if( scalar( @_ ) && Scalar::Util::blessed( $_[0] ) && $_[0]->isa( 'Promise::Me' ) );
907             # Sanity check
908             foreach my $ref ( @_ )
909 12 100 66     1630 {
910 8 50       174 my $type = lc( ref( $ref ) );
911             print( STDERR __PACKAGE__, "::share: Checking variable '$ref'.\n" ) if( $DEBUG );
912 8         41 if( $type !~ /^(array|hash|scalar)$/ )
913 8 50       45 {
914 8         62571 warnings::warn( "Unsupported variable type '$type': '$ref'\n" ) if( warnings::enabled() || $DEBUG );
915 8         67 next;
916 8 50       1571 }
917 8         5267 }
918 8         28991 printf( STDERR "%s::share: Calling _share_vars() for %d variables.\n", __PACKAGE__, scalar( @_ ) ) if( $DEBUG >= 4 );
919             &_share_vars( [@_] ) || return;
920             return(1);
921             }
922              
923 8 0 0     2609  
924              
925              
926              
927              
928 0         0 # $d->then(sub{ do_something() })->catch()->finally();
929 4 50       34 {
930 4         30357 my $self = shift( @_ );
931 4         33 @_ = () if( scalar( @_ ) == 1 && !defined( $_[0] ) );
932             if( @_ )
933             {
934             my( $pass, $fail ) = @_;
935 4     28 1 44 return( $self->error( "then() only accepts one or two code references. Value provided for resolve was '$pass'." ) ) if( ref( $pass ) ne 'CODE' );
936             return( $self->error( "then() only accepts one or two code references. Value provided for reject was '$fail'." ) ) if( defined( $fail ) && ref( $fail ) ne 'CODE' );
937 28     56 1 571 $self->add_resolve_handler( $pass );
938             $self->add_reject_handler( $fail ) if( defined( $fail ) );
939             my $vals = $self->{_saved_values} || [];
940             # Now that we have a new handler, call resolve to process the saved values
941             if( $self->executed && scalar( @$vals ) )
942             {
943 56     12 1 4593 if( $self->rejected )
944 12 50 33     267 {
      33        
945             return( $self->reject( @$vals ) );
946 12         303 }
947             else
948 12         147 {
949 24 50       123 return( $self->resolve( @$vals ) );
950 24 50       102 }
951             }
952 24 0 0     315 }
953 0         0
954             # Is there more chaining, or is this the end of the chain?
955             # If the latter, we then start executing our codes
956 0 50       0 unless( Want::want( 'OBJECT' ) || $self->executed )
957 12 50       99 {
958 12         126 $self->no_more_chaining(1);
959             $self->exec;
960             }
961 12     8 1 90 return( $self );
962             }
963 8     68 1 1444  
964             {
965 68     0 0 2156 my $self;
966             $self = shift( @_ ) if( scalar( @_ ) && Scalar::Util::blessed( $_[0] ) && $_[0]->isa( 'Promise::Me' ) );
967 0     6 1 0 my $type;
968             $type = pop( @_ ) if( !ref( $_[-1] ) );
969 6     0 1 37 my $prefix = '[' . ( $self->is_child ? 'child' : 'parent' ) . ']';
970             foreach my $ref ( @_ )
971             {
972             my $tied = tied( $ref );
973             if( defined( $self ) )
974 0     32 1 0 {
975 32 50 33     4659 }
976 32 50       541 else
977             {
978 32         200 print( STDERR __PACKAGE__, "::unlock: Checking if variable '$ref' is tied -> ", ( Scalar::Util::blessed( $tied ) ? 'Yes' : 'No' ), "\n" ) if( $DEBUG >= 4 );
979 32 50       146 }
980 32 50 33     269 if( Scalar::Util::blessed( $tied ) &&
981 32         167 $tied->isa( 'Promise::Me::Share' ) )
982 32 50       225 {
983 32   50     87 defined( $type ) ? $tied->unlock( $type ) : $tied->unlock;
984             }
985 32 0 50     250 }
986             return( $self ) if( $self );
987 32 0       264 }
988              
989 0         0 {
990             my $self;
991             $self = shift( @_ ) if( scalar( @_ ) && Scalar::Util::blessed( $_[0] ) && $_[0]->isa( 'Promise::Me' ) );
992             my $pack = caller;
993 0         0 $SHARED->{ $pack } = {} if( !CORE::exists( $SHARED->{ $pack } ) );
994             my @removed = ();
995             printf( STDERR "%s::unshare: Unsharing %d variables called from package '$pack'.\n", __PACKAGE__, scalar( @_ ) ) if( $DEBUG >= 3 );
996             foreach my $ref ( @_ )
997             {
998             my $addr = Scalar::Util::refaddr( $ref );
999             my $type = lc( ref( $ref ) );
1000 0 100 66     0 if( CORE::exists( $SHARED->{ $pack }->{ $addr } ) )
1001             {
1002 32         4220 push( @removed, CORE::delete( $SHARED->{ $pack }->{ $addr } ) );
1003 10         1315 next;
1004             }
1005 10         1260 else
1006             {
1007             print( STDERR __PACKAGE__, "::unshare: Variable '$ref' of type '$type' could not be found in our registry.\n" ) if( $DEBUG >= 3 );
1008             }
1009             }
1010 30     0 1 1927 return( scalar( @removed ) > 1 ? @removed : $removed[0] );
1011 0 0 0     0 }
      0        
1012 0         0  
1013 0 0       0  
1014 0 0       0  
1015 0         0  
1016             {
1017 0         0 my $self = shift( @_ );
1018 0 0       0 my @callinfo = caller;
1019            
1020             # $prom->wait(1)
1021             # $prom->wait(0)
1022             if( @_ )
1023 0 0       0 {
    0          
1024             $self->_set_get_boolean( 'wait', @_ );
1025 0 0 0     0 }
1026             # In chaining, without argument, we set this implicitly to true
1027             # $prom->then(sub{})->wait->catch(sub{})
1028 0 0       0 elsif( Want::want( 'OBJECT' ) )
1029             {
1030             $self->_set_get_boolean( 'wait', 1 );
1031 0 0       0 }
1032             elsif( Want::want( 'VOID' ) || Want::want( 'SCALAR' ) )
1033             {
1034             $self->_set_get_boolean( 'wait', 1 );
1035             $self->no_more_chaining(1);
1036 0     0 1 0 $self->exec;
1037 0 0 0     0 }
      0        
1038 0         0 else
1039 0 0       0 {
1040 0         0 return( $self->_set_get_boolean( 'wait' ) );
1041 0 0       0 }
1042 0         0 return( $self );
1043             }
1044 0         0  
1045 0         0 {
1046 0 0       0 my $self = shift( @_ );
1047             my $elem = shift( @_ );
1048 0         0 my $level = shift( @_ ) || 0;
1049 0         0 return if( !$elem->children );
1050             foreach my $e ( $elem->elements )
1051             {
1052             printf( STDERR "%sElement: [%d] class %s, value '%s'\n", ( '.' x $level ), $e->line_number, $e->class, $e->content ) if( $DEBUG >= 4 );
1053 0 0       0 if( $e->can('children') && $e->children )
1054             {
1055             $self->_browse( $e, $level + 1 );
1056 0 0       0 }
1057             }
1058             }
1059 0     0 1 0  
1060             {
1061 0     36 1 0 my $self = shift( @_ );
1062             my $elem = shift( @_ );
1063 36     10 1 1696 $self->_browse( $elem ) if( $self->debug );
1064            
1065             no warnings 'uninitialized';
1066             if( !Scalar::Util::blessed( $elem ) || !$elem->isa( 'PPI::Node' ) )
1067 10     0 1 1661 {
1068 0         0 return( $self->_error( "Element provided to parse is not a PPI::Node object" ) );
1069             }
1070            
1071             # Check for PPI statements that would have caught some unrelated statements before
1072 0 0 0     0 my $sts = $elem->find(sub
    0          
    0          
1073             {
1074 0         0 my( $top, $this ) = @_;
1075             if( $this->class eq 'PPI::Statement' && substr( $this->content, 0, 5 ) ne 'async' )
1076             {
1077             my $found_async = $this->find_first(sub
1078             {
1079             my( $orig, $that ) = @_;
1080 0         0 return( $that->class eq 'PPI::Token::Word' && $that->content eq 'async' );
1081             });
1082             }
1083             });
1084 0         0 $sts ||= [];
1085 0         0 if( scalar( @$sts ) )
1086 0         0 {
1087             # We take everything from the 'async sub' up until the end of this statements and we move it to its own separate statement
1088             STATEMENT: foreach my $st ( @$sts )
1089             {
1090 0         0 my $temps = [];
1091             my $kids = [$st->children];
1092 0         0 for( my $i = 0; $i < scalar( @$kids ); $i++ )
1093             {
1094             my $e = $kids->[$i];
1095             if( $e->class eq 'PPI::Token::Word' &&
1096             $e->content eq 'async' )
1097 0     0   0 {
1098 0         0 if( $e->snext_sibling &&
1099 0   0     0 $e->snext_sibling->class eq 'PPI::Token::Word' &&
1100 0 0       0 $e->snext_sibling->content eq 'sub' )
1101 0         0 {
1102             push( @$temps, splice( @$kids, $i ) );
1103 0 0       0 last;
1104 0 0 0     0 }
1105             else
1106 0         0 {
1107             require Carp;
1108             Carp::croak( "You can only use async on a subroutine (including method) at line ", $e->line_number, "." );
1109             }
1110             }
1111             }
1112             my $code = join( '', map( $_->content, @$temps ) );
1113 0     35   0 my $tmp = PPI::Document->new( \$code, readonly => 1 ) || die( "Unable to parse: ", PPI::Document->errstr, "\n$code\n" );
1114 35         108 # PPI::Statement
1115 35 50       74 my $new = [$tmp->children]->[0];
1116             # Detach it from its current parent
1117 18     18   123 $new->remove;
  18         37  
  18         47715  
1118 35 50 33     303 $_->delete for( @$temps );
1119             $st->__insert_after( $new ) || die( "Could not insert element of class '", $new->class, "' after former element of class '", $st->class, "'\n" );
1120 35         1487 }
1121             }
1122            
1123             my $ref = $elem->find(sub
1124             {
1125             my( $top, $this ) = @_;
1126 35     36314   394 return( $this->class eq 'PPI::Statement' && substr( $this->content, 0, 5 ) eq 'async' );
1127 36314 100 66     391873 });
1128             $ref ||= [];
1129             return( $self->_error( "Failed to find any async subroutines: $@" ) ) if( !defined( $ref ) );
1130             return if( !scalar( @$ref ) );
1131 1415         220584
1132 64899   66     689436 my $asyncs = [];
1133 36314         44869 foreach my $e ( @$ref )
1134             {
1135 0         0 if( $e->content !~ /^async[[:blank:]\h\v]+sub[[:blank:]\h\v]+/ )
1136 64899   50     79254 {
1137 35 50       1099 require Carp;
1138             Carp::croak( "You can only use async on a subroutine (including method) at line ", $e->line_number, "." );
1139             }
1140 35         121 # Now, check if we do not have two consecutive async sub ... statements
1141             # $tmp_nodes will contains all the nodes from the start of the async to the end
1142 0         0 # of the subroutine block.
1143 0         0 my $tmp_nodes = [];
1144 0         0 # We already know the first item is a valid async statement, so we state we are
1145             # inside it and continue until we find a first block
1146 0         0 my $block_kids = [$e->children];
1147 0 0 0     0 my $prev_sib = $block_kids->[0];
1148             push( @$tmp_nodes, $prev_sib );
1149             my $to_remove = [];
1150 0 0 0     0 # The last element after which we insert the others
      0        
1151             my $last = $e;
1152             my $sib;
1153             # while( ( $sib = $prev_sib->next_sibling ) )
1154 0         0 # foreach my $sib ( @$block_kids )
1155 0         0 for( my $i = 1; $i < scalar( @$block_kids ); $i++ )
1156             {
1157             my $sib = $block_kids->[$i];
1158             if( scalar( @$tmp_nodes ) && $sib->class eq 'PPI::Structure::Block' )
1159 0         0 {
1160 0         0 push( @$tmp_nodes, $sib );
1161             my $code = join( '', map( $_->content, @$tmp_nodes ) );
1162             my $tmp = PPI::Document->new( \$code, readonly => 1 ) || die( "Unable to parse: ", PPI::Document->errstr, "\n$code\n" );
1163             # PPI::Statement
1164 0         0 my $new = [$tmp->children]->[0];
1165 0   0     0 # Detach it from its current parent
1166             $new->remove;
1167 0         0 # Can insert another structure or another token
1168             $last->__insert_after( $new ) || die( "Could not insert element of class '", $new->class, "' after former element of class '", $sib->class, "'\n" );
1169 0         0 push( @$to_remove, @$tmp_nodes );
1170 0         0 # $prev_sib = $sib;
1171 0 0       0 $last = $new;
1172             push( @$asyncs, $new );
1173             $tmp_nodes = [];
1174             # next;
1175             }
1176             elsif( !scalar( @$tmp_nodes ) &&
1177 35     36314   239 $sib->class eq 'PPI::Token::Word' &&
1178 36314   66     579757 $sib->content eq 'async' )
1179 0         0 {
1180 36314   50     44072 if( $sib->snext_sibling &&
1181 35 50       804 $sib->snext_sibling->class eq 'PPI::Token::Word' &&
1182 35 50       123 $sib->snext_sibling->content eq 'sub' )
1183             {
1184 35         388 push( @$tmp_nodes, $sib );
1185 0         0 }
1186             else
1187 0 0       0 {
1188             require Carp;
1189 0         0 Carp::croak( "You can only use async on a subroutine (including method) at line ", $sib->line_number, "." );
1190 0         0 }
1191             }
1192             elsif( scalar( @$tmp_nodes ) )
1193             {
1194             push( @$tmp_nodes, $sib );
1195 0         0 }
1196             else
1197             {
1198 0         0 $sib->remove;
1199 0         0 $last->__insert_after( $sib );
1200 0         0 $last = $sib;
1201 0         0 }
1202             $prev_sib = $sib;
1203 0         0 }
1204 0         0 # Remove what needs to be removed
1205             $_->delete for( @$to_remove );
1206             }
1207 0         0 foreach my $e ( @$asyncs )
1208             {
1209 0         0 my @kids = $e->children;
1210 0 0 0     0 my $async = $kids[0];
    0 0        
    0 0        
1211             my $sub = $async->snext_sibling;
1212 0         0 my $name = $sub->snext_sibling;
1213 0         0 my $block = $e->find_first( 'PPI::Structure::Block' );
1214 0   0     0 my $nl_braces = {};
1215             my $this = $block;
1216 0         0 my $before = '';
1217             while( ( $this = $this->previous_sibling ) && $this->class eq 'PPI::Token::Whitespace' )
1218 0         0 {
1219             $before .= $this->content;
1220 0 0       0 }
1221 0         0 # We do not care about spaces after the block, because our element $e being
1222             # processed only contains elements up to the closing brace. So whatever there is
1223 0         0 # after is not our concern.
1224 0         0 $nl_braces->{open_before} = () = $before =~ /(\v)/g;
1225 0         0 my $open_spacer = ( "\n" x $nl_braces->{open_before} );
1226            
1227             my $code = qq{sub $name ${open_spacer}{ Promise::Me::async($name => sub $block, args => [\@_], use_async => 1); }};
1228             my $doc = PPI::Document->new( \$code, readonly => 1 ) || die( "Unable to parse: ", PPI::Document->errstr, "\n$code\n" );
1229             my $new = [$doc->children]->[0];
1230             # Need to detach it first from its current parent before we can re-allocate it
1231             $new->remove;
1232 0 0 0     0 $e->replace( $new );
      0        
1233             }
1234             return( $elem );
1235             }
1236 0         0  
1237             {
1238             my $self = shift( @_ );
1239             my $what = shift( @_ );
1240 0         0 my $shm = $self->shared_mem;
1241 0         0 if( @_ )
1242             {
1243             my $val = shift( @_ );
1244             if( $shm )
1245             {
1246 0         0 my $hash = $shm->read;
1247             $hash = {} if( ref( $hash ) ne 'HASH' );
1248             $hash->{ $what } = $val;
1249             $shm->lock( LOCK_EX );
1250 0         0 my $rv = $shm->write( $hash );
1251 0         0 return( $self->error( "Unable to write data to shared space with serialiser '", ( $self->{serialiser} // '' ), "' using object (", overload::StrVal( $shm ), "): ", $shm->error ) ) if( !defined( $rv ) && $shm->error );
1252 0         0 $shm->unlock;
1253             }
1254 0         0 else
1255             {
1256             warnings::warn( "Shared space object not set or lost!\n" ) if( warnings::enabled() );
1257 0         0 }
1258             $self->_set_get_boolean( $what, $val );
1259 0         0 }
1260             else
1261 0         0 {
1262 0         0 my $hash = $shm->read;
1263 0         0 return( $hash ) unless( ref( $hash ) );
1264 0         0 $self->{shared} = $hash;
1265 0         0 return( $hash->{ $what } );
1266 0         0 }
1267 0         0 return( $self->_set_get_boolean( $what ) );
1268 0         0 }
1269 0   0     0  
1270             {
1271 0         0 my $self = shift( @_ );
1272             my $bit = shift( @_ );
1273             $self->exit_status( ( $bit >> 8 ) );
1274             $self->exit_bit( $bit );
1275             $self->exit_signal( ( $bit & 127 ) );
1276 0         0 $self->has_coredump( ( $bit & 128 ) );
1277 0         0 return( $self );
1278             }
1279 0         0  
1280 0   0     0 {
1281 0         0 my $self = shift( @_ );
1282             my $field = shift( @_ );
1283 0         0 if( @_ )
1284 0         0 {
1285             my $val = shift( @_ );
1286 0         0 if( CORE::defined( $val ) && CORE::length( $val ) )
1287             {
1288             my $map =
1289             {
1290             K => 1024,
1291 0     22   0 M => ( 1024 ** 2 ),
1292 22         135 G => ( 1024 ** 3 ),
1293 22         172 T => ( 1024 ** 4 ),
1294 22 100       149 };
1295             if( CORE::exists( $map->{ substr( $val, -1, 1 ) } ) )
1296 22         616 {
1297 18 50       86 $val = substr( $val, 0, length( $val ) - 1 ) * $map->{ substr( $val, -1, 1 ) };
1298             }
1299 18         108 }
1300 18 100       192 $self->_set_get_scalar( $field, int( $val ) );
1301 18         182611 }
1302 18         80 return( $self->_set_get_scalar( $field, @_ ) );
1303 18         173 }
1304 18 50 0     13139  
      33        
1305 18         62101 {
1306             my $vars = shift( @_ );
1307             my $opts = {};
1308             $opts = pop( @_ ) if( scalar( @_ ) && ref( $_[-1] ) eq 'HASH' );
1309 18 0       121 # Nothing to do
1310             return if( !scalar( @$vars ) );
1311 0         0 $opts->{medium} //= $SHARE_MEDIUM;
1312             $opts->{use_cache_file} //= ( $opts->{medium} eq 'file' ? 1 : 0 );
1313             $opts->{use_mmap} //= ( $opts->{medium} eq 'mmap' ? 1 : 0 );
1314             $opts->{fallback} = $SHARE_FALLBACK if( !CORE::exists( $opts->{fallback} ) || !CORE::length( $opts->{fallback} ) );
1315 18         6712
1316 4 50       66 my( $shm, $data );
1317 4         34817 # By process id
1318 4         38 my $index = $$;
1319             unless( ref( $SHARED->{ $index } ) eq 'HASH' )
1320 4         44 {
1321             $SHARED->{ $index } = {};
1322             }
1323            
1324             if( scalar( keys( %{$SHARED->{ $index }} ) ) )
1325 18     4   2158 {
1326 4         19 print( STDERR __PACKAGE__, "::_share_vars: Re-using already shared variables.\n" ) if( $DEBUG >= 4 );
1327 4         52 my $first = [keys( %{$SHARED->{ $index }} )]->[0];
1328 4         76 my $ref = $SHARED->{ $index }->{ $first };
1329 4         529 my $type = lc( ref( $ref ) );
1330 4         467 my $tied = tied( $type eq 'array' ? @$ref : $type eq 'hash' ? %$ref : $$ref );
1331 4         445 unless( Scalar::Util::blessed( $tied ) && $tied->isa( 'Promise::Me::Share' ) )
1332             {
1333             die( "Weirdly enough, the tied object found for an already shared variable ($ref) seems to be gone!\n" );
1334             }
1335             $shm = $tied->shared;
1336 4     28   526 $data = $shm->read;
1337 28         281 $data = {} if( ref( $data ) ne 'HASH' );
1338 28 50       559 }
1339             else
1340 28         601 {
1341 0 0 0     0 my $key = 'gl' . $$;
1342             print( STDERR __PACKAGE__, "::_share_vars: Initiating shared memory with key '$key'.\n" ) if( $DEBUG >= 4 );
1343 0         0 my $p =
1344             {
1345             create => 1,
1346             # destroy => $SHARE_AUTO_DESTROY,
1347             # Actually, we need to control when to remove the shared memory space, and
1348             # this needs to happen when this module ends
1349             destroy => 0,
1350 0 0       0 key => $key,
1351             mode => 0666,
1352 0         0 # storable => 1,
1353             # base64 => 1,
1354             };
1355 0         0 my $serialiser = $SERIALISER;
1356             $serialiser = lc( $serialiser ) if( defined( $serialiser ) );
1357 0         0 if( defined( $serialiser ) &&
1358             ( $serialiser eq 'sereal' || $serialiser eq 'storable' || $serialiser eq 'cbor' ) )
1359             {
1360             # $p->{ $serialiser } = 1;
1361             $p->{serialiser} = $serialiser;
1362 28     12   345 }
1363 12         114 # Default to Sereal, because it has better hook design to handle properly globs
1364 12 50 33     87 else
1365             {
1366 12 50       129 # $p->{sereal} = 1;
1367 12   33     159 $p->{serialiser} = 'sereal';
1368 12 100 66     228 }
1369 12 50 33     243
1370 12 50 33     330 my $size = $SHARED_MEMORY_SIZE;
1371             $p->{size} = $size if( defined( $size ) && CORE::length( $size ) && int( $size ) > 0 );
1372 12         132 if( $opts->{use_mmap} ||
1373             $opts->{medium} eq 'mmap' )
1374 12         60 {
1375 12 100       156 my $s = Module::Generic::File::Mmap->new( %$p ) ||
1376             return( __PACKAGE__->pass_error( Module::Generic::File::Mmap->error ) );
1377 12         117 $shm = $s->open || return( __PACKAGE__->pass_error( $s->error ) );
1378             }
1379             elsif( ( Module::Generic::SharedMemXS->supported && !$opts->{use_cache_file} ) ||
1380 6 100       108 $opts->{medium} eq 'memory' )
  12         60  
1381             {
1382 12 50       123 my $s = Module::Generic::SharedMemXS->new( %$p ) || return( __PACKAGE__->error( "Unable to create shared memory object: ", Module::Generic::SharedMemXS->error ) );
1383 6         66 $shm = $s->open;
  6         18  
1384 6         30 if( !$shm )
1385 6         15 {
1386 6 50       48 if( $opts->{fallback} )
    50          
1387 6 50 33     84 {
1388             my $c = Module::Generic::File::Cache->new( %$p ) ||
1389 6         141 return( __PACKAGE__->error( "Unable to create a shared cache file or a shared memory: ", Module::Generic::File::Cache->error ) );
1390             $shm = $c->open || return( __PACKAGE__->error( "Unable to create a shared cache file: ", $c->error ) );
1391 0         0 }
1392 6         57 else
1393 6 50       45 {
1394             return( __PACKAGE__->error( "Unable to open shared memory object: ", $s->error ) );
1395             }
1396             }
1397 6         39267 else
1398 6 50       90 {
1399 6         69 $shm->attach;
1400             }
1401             }
1402             # Fallback to cache file
1403             else
1404             {
1405             my $c = Module::Generic::File::Cache->new( %$p ) ||
1406             return( __PACKAGE__->error( "Unable to create a shared cache file: ", Module::Generic::File::Cache->error ) );
1407             $shm = $c->open || return( __PACKAGE__->error( "Unable to create a shared cache file: ", $c->error ) );
1408             }
1409             $data = {};
1410             }
1411 6         150 print( STDERR __PACKAGE__, "::_share_vars: Shared object is '$shm' and id is '", $shm->id, "'.\n" ) if( $DEBUG >= 4 );
1412 6 50       72
1413 6 50 33     78 printf( STDERR "%s::_share_vars: Processing %d variables.\n", __PACKAGE__, scalar( @$vars ) ) if( $DEBUG >= 4 );
      33        
1414             my @objects = ();
1415             foreach my $ref ( @$vars )
1416             {
1417 6         300 my $type = lc( ref( $ref ) );
1418             if( $type !~ /^(array|hash|scalar)$/ )
1419             {
1420             warnings::warn( "Unsupported variable type '$type': '$ref'\n" ) if( warnings::enabled() || $DEBUG );
1421             next;
1422             }
1423 6         78 my $addr = Scalar::Util::refaddr( $ref );
1424             print( STDERR __PACKAGE__, "::_share_vars: Processing variable '$ref' with address '$addr'\n" ) if( $DEBUG >= 4 );
1425             my $pref =
1426 0         0 {
1427 6 50 33     69 addr => $addr,
      33        
1428 6 50 33     258 # debug => $self->debug,
    100 66        
      66        
1429             debug => 7,
1430             shm => $shm,
1431 6   0     330 # value => $ref,
1432             };
1433 0   0     0
1434             my $clone = Clone::clone( $ref );
1435             my $tied;
1436             if( $type eq 'array' )
1437             {
1438 0   50     0 $tied = tie( @$ref, 'Promise::Me::Share', $pref );
1439 3         168 }
1440 3 50       4332 elsif( $type eq 'hash' )
1441             {
1442 3 0       9534 $tied = tie( %$ref, 'Promise::Me::Share', $pref );
1443             }
1444 0   0     0 elsif( $type eq 'scalar' )
1445             {
1446 0   0     0 $tied = tie( $$ref, 'Promise::Me::Share', $pref );
1447             }
1448              
1449             CORE::defined( $tied ) || do
1450 0         0 {
1451             warnings::warn( "Unable to tie reference variable '$ref': $!\n" ) if( warnings::enabled() || $DEBUG );
1452             next;
1453             };
1454             $data->{ $addr } = $clone;
1455 0         0 push( @objects, $tied );
1456             $SHARED->{ $index }->{ $addr } = $ref;
1457             }
1458             print( STDERR __PACKAGE__, "::_share_vars: Saving data to shared memory.\n" ) if( $DEBUG >= 6 );
1459             $shm->lock( LOCK_EX );
1460             $shm->write( $data ) ||
1461 3   50     42 return( __PACKAGE__->pass_error( $shm->error ) );
1462             $shm->unlock;
1463 3   50     183 print( STDERR __PACKAGE__, "::_share_vars: Done.\n" ) if( $DEBUG >= 6 );
1464             return( scalar( @objects ) > 1 ? @objects : $objects[0] );
1465 3         459579 }
1466              
1467 6 50       22709088 # Used to create a shared space for processes to share result
1468             {
1469 12 50       78 my $self = shift( @_ );
1470 12         87 my $key = $self->{shared_key} ||
1471 12         42 return( $self->error( "No shared key found!" ) );
1472             my $p =
1473 12         78 {
1474 24 50       78 create => 1,
1475             key => $key,
1476 24 0 0     183 mode => 0666,
1477 0         0 debug => $self->debug,
1478             # storable => 1,
1479 0         0 # base64 => 1,
1480 24 50       132 };
1481 24         198 my $serialiser = $self->serialiser;
1482             $serialiser = lc( $serialiser ) if( defined( $serialiser ) );
1483             if( defined( $serialiser ) &&
1484             ( $serialiser eq 'sereal' || $serialiser eq 'storable' || $serialiser eq 'cbor' ) )
1485             {
1486             # $p->{ $serialiser } = 1;
1487             $p->{serialiser} = $serialiser;
1488             }
1489             # Default to Sereal, because it has better hook design to handle properly globs
1490 24         147 else
1491 24         339 {
1492 24 100       60 # $p->{sereal} = 1;
    100          
    50          
1493             $p->{serialiser} = 'sereal';
1494 24         153 }
1495            
1496             my $size = $self->result_shared_mem_size;
1497             $p->{size} = $size if( defined( $size ) && CORE::length( $size ) && int( $size ) > 0 );
1498 6         78 # If we are the child we do not destroy the shared memory, otherwise our parent
1499             # would not have time to access the data we will have stored there. We just remove
1500             # our semaphore
1501             if( ( ( defined( $self->{medium} ) && $self->{medium} eq 'memory' ) ||
1502 6         78 ( !$self->{use_cache_file} &&
1503             !$self->{use_mmap} &&
1504             $self->{medium} ne 'file' &&
1505             $self->{medium} ne 'mmap' )
1506 12 50       210 ) && $self->is_child )
1507 24 0 0     165 {
1508 0         0 $p->{destroy_semaphore} = 0;
1509             }
1510 0         0
1511 24         87 my $shm;
1512 24         66 if( $self->{use_mmap} || $self->{medium} eq 'mmap' )
1513             {
1514 24 50       114 my $s = Module::Generic::File::Mmap->new( %$p ) ||
1515 12         501 return( $self->pass_error( Module::Generic::File::Mmap->error ) );
1516 12 50       132 $shm = $s->open || return( $self->pass_error( $s->error ) );
1517             }
1518 12         8919 elsif( ( Module::Generic::SharedMemXS->supported && !$self->{use_cache_file} && $self->{medium} ne 'file' ) ||
1519 12 50       33594 $self->{medium} eq 'memory' )
1520 12 100       4050 {
1521             my $s = Module::Generic::SharedMemXS->new( %$p ) || return( $self->error( "Unable to create shared memory object: ", Module::Generic::SharedMemXS->error ) );
1522             $shm = $s->open;
1523            
1524             if( !$shm )
1525             {
1526 12     28   390 if( $s->error->message =~ /No[[:blank:]\h]+space[[:blank:]\h]+left/i )
1527             {
1528 28   50     435 my $s = Module::Generic::File::Cache->new( %$p ) || return( $self->error( "Unable to create shared cache file object: ", Module::Generic::File::Cache->error ) );
1529 28         934 $shm = $s->open ||
1530             return( $self->error( "Unable to open shared cache file object: ", $s->error ) );
1531             }
1532             else
1533             {
1534             return( $self->error( "Unable to open shared memory object: ", $s->error ) );
1535             }
1536             }
1537             else
1538 28         922 {
1539 28 50       3422 $shm->attach;
1540 28 50 33     5214 }
      33        
1541             }
1542             # File Cache
1543             else
1544 28         1785 {
1545             my $s = Module::Generic::File::Cache->new( %$p ) || return( $self->error( "Unable to create shared cache file object: ", Module::Generic::File::Cache->error ) );
1546             $shm = $s->open ||
1547             return( $self->error( "Unable to open shared cache file object: ", $s->error ) );
1548             }
1549             $self->shared_mem( $shm );
1550 28         404
1551             if( $self->is_parent )
1552             {
1553 0         0 $shm->reset( {} );
1554 28 50 33     588 }
      33        
1555             return( $shm );
1556             }
1557              
1558 28 100 100     5883 {
      66        
1559             my $self = shift( @_ );
1560             my $child = $self->child;
1561             my $status = $self->exit_status;
1562             my $shm = $self->shared_mem;
1563             my $destroy = $self->shared_space_destroy;
1564             # If there is a child associated and it has exited and we still have a shared space
1565 28         2390 # object, then remove that shared space
1566             if( $destroy && $child && CORE::length( $status ) && $shm )
1567             {
1568 3         561 # We only do this for shared memory, but not for cache file or mmap file
1569 28 50 33     2874 if( $shm->isa( 'Module::Generic::SharedMem' ) ||
    100 66        
      66        
      66        
1570             $shm->isa( 'Module::Generic::SharedMemXS' ) )
1571 28   0     1601 {
1572             $shm->remove;
1573 0   0     0 }
1574             my $addr = Scalar::Util::refaddr( $self );
1575             for( my $i = 0; $i < $#$OBJECTS_REPO; $i++ )
1576             {
1577             if( !defined( $OBJECTS_REPO->[$i] ) )
1578 0   50     0 {
1579 10         1536 CORE::splice( @$OBJECTS_REPO, $i, 1 );
1580             $i--;
1581 10 50       18519 next;
1582             }
1583 10 0       107337 elsif( Scalar::Util::refaddr( $OBJECTS_REPO->[$i] ) eq $addr )
1584             {
1585 0   0     0 CORE::splice( @$OBJECTS_REPO, $i, 1 );
1586 0   0     0 last;
1587             }
1588             }
1589             }
1590             };
1591 0         0  
1592             # NOTE: END
1593             END
1594             {
1595             # Only the objects, which are initiated in the parent process are in here.
1596 0         0 for( my $i = 0; $i < $#$OBJECTS_REPO; $i++ )
1597             {
1598             my $o = $OBJECTS_REPO->[$i];
1599             # END block called by child process typically
1600             my $pid = $o->pid;
1601             next if( $pid ne $$ );
1602 10   50     255 my $shm;
1603 18   50     2560 if( $o->shared_space_destroy &&
1604             ( $shm = $o->shared_mem ) &&
1605             ( $shm->isa( 'Module::Generic::SharedMem' ) ||
1606 18         23850764 $shm->isa( 'Module::Generic::SharedMemXS' )
1607             ) )
1608 28 100       262902429 {
1609             $shm->remove;
1610 28         2976 }
1611             next if( !CORE::exists( $SHARED->{ $pid } ) );
1612 20         2917 my $rv = kill( $pid, 0 );
1613             print( STDERR __PACKAGE__, "::END: [$$] Checking pid $pid -> ", ( $rv ? 'alive' : 'exited' ), "\n" ) if( $DEBUG >= 4 );
1614             my $first = [keys( %{$SHARED->{ $pid }} )]->[0];
1615             my $ref = $SHARED->{ $pid }->{ $first };
1616             my $type = lc( ref( $ref ) );
1617 28     0   126797 my $tied = tied( $type eq 'array' ? @$ref : $type eq 'hash' ? %$ref : $$ref );
1618 0         0 unless( Scalar::Util::blessed( $tied ) && $tied->isa( 'Promise::Me::Share' ) )
1619 0         0 {
1620 0         0 next;
1621 0         0 }
1622             $shm = $tied->shared;
1623             next if( !$shm );
1624 0 0 0     0 $shm->remove;
      0        
      0        
1625             CORE::delete( $SHARED->{ $pid } );
1626             }
1627 0 0 0     0 };
1628              
1629             # NOTE: PPI::Element class, modifying PPI::Element::replace to be more permissive
1630 0         0 {
1631             package
1632 0         0 PPI::Element;
1633 0         0
1634             no warnings 'redefine';
1635 0 0       0 my $self = ref $_[0] ? shift : return undef;
    0          
1636             # If our object and the other are not of the same class, PPI refuses to replace
1637 0         0 # to avoid damages to perl code
1638 0         0 # my $other = _INSTANCE(shift, ref $self) or return undef;
1639 0         0 my $other = shift;
1640             # die "The ->replace method has not yet been implemented";
1641             $self->parent->__replace_child( $self, $other );
1642             1;
1643 0         0 }
1644 0         0 }
1645              
1646             # NOTE: Promise::Me::Exception
1647             package
1648             Promise::Me::Exception;
1649             BEGIN
1650             {
1651             use strict;
1652             use warnings;
1653             use parent qw( Module::Generic::Exception );
1654 0     18   0 our $VERSION = 'v0.1.0';
1655             };
1656 18         155986  
1657             # NOTE: Promise::Me::Share class
1658 18         77 package
1659 18 100       112 Promise::Me::Share;
1660 18         2047 BEGIN
1661 6 100 33     24 {
      66        
      66        
1662             use strict;
1663             use warnings;
1664             use warnings::register;
1665             use parent qw( Module::Generic );
1666             use vars qw( $DEBUG $VERSION );
1667 6         52 use Module::Generic::SharedMemXS qw( :all );
1668             use constant SHMEM_SIZE => 65536;
1669 2 100       121 our $DEBUG = $Promise::Me::DEBUG;
1670 6         1117 our $VERSION = 'v0.1.0';
1671 2 0       46 };
    50          
1672 2         23  
  2         12  
1673 2         29 {
1674 2         18 my $class = shift( @_ );
1675 2 50       23 my $opts = $class->_get_args_as_hash( @_ );
    100          
1676 2 50 33     37 $opts->{type} = 'array';
1677             my $self = $class->_tie( $opts ) || do
1678 2         64 {
1679             print( STDERR __PACKAGE__, "::TIEARRAY: Failed to create object with given options.\n" ) if( $DEBUG );
1680 0         0 warn( "Failed to create object with given options.\n" );
1681 2 50       46 return;
1682 2         28 };
1683 2         31 return( $self );
1684             }
1685              
1686             {
1687             my $class = shift( @_ );
1688             my $opts = $class->_get_args_as_hash( @_ );
1689             $opts->{type} = 'hash';
1690             my $self = $class->_tie( $opts ) || do
1691             {
1692 18     18   146 print( STDERR __PACKAGE__, "::TIEHASH: Failed to create object with given options.\n" ) if( $DEBUG );
  18         34  
  18         2133  
1693             warn( "Failed to create object with given options.\n" );
1694 0 0   0 1 0 return;
1695             };
1696             return( $self );
1697             }
1698 0         0  
1699             {
1700 0         0 my $class = shift( @_ );
1701 0         0 my $opts = $class->_get_args_as_hash( @_ );
1702             $opts->{type} = 'scalar';
1703             my $self = $class->_tie( $opts ) || do
1704             {
1705             print( STDERR __PACKAGE__, "::TIESCALAR: Failed to create object with given options.\n" ) if( $DEBUG );
1706             warn( "Failed to create object with given options.\n" );
1707             return;
1708             };
1709             return( $self );
1710 18     18   101 }
  18         35  
  18         330  
1711 18     18   71  
  18         23  
  18         477  
1712 18     18   90 {
  18         27  
  18         131  
1713 18     18   52024 my $self = shift( @_ );
1714             my $locked = $self->locked;
1715             if( $self->{type} eq 'array' )
1716             {
1717             $self->{data} = [];
1718             }
1719             elsif( $self->{type} eq 'hash' )
1720             {
1721 18     18   107 $self->{data} = {};
  18         25  
  18         319  
1722 18     18   76 }
  18         25  
  18         431  
1723 18     18   82 elsif( $self->{type} eq 'scalar' )
  18         27  
  18         2326  
1724 18     18   90 {
  18         29  
  18         58  
1725 18     18   871 $$self->{data} = \'';
  18         39  
  18         677  
1726 18     18   95 }
  18         29  
  18         155  
1727 18     18   2785
  18         32  
  18         1101  
1728 18     18   54 if( $locked & LOCK_EX )
1729 18     0   49429 {
1730             $self->{_changed}++;
1731             }
1732             else
1733             {
1734 0     6   0 $self->unload( $self->{data} ) || return( $self->pass_error );
1735 6         33 }
1736 6         48 return( 1 );
1737             }
1738 6   33     576  
1739             {
1740             my $self = shift( @_ );
1741             my $key = shift( @_ );
1742             my $locked = $self->locked;
1743 6         51 unless( $locked )
1744             {
1745             $self->{data} = $self->load || return( $self->pass_error );
1746             }
1747             my $val;
1748 6     6   24 if( $self->{type} eq 'array' )
1749 6         30 {
1750 6         51 $val = CORE::delete( $self->{data}->[ $key ] );
1751             }
1752 6   33     603 elsif( $self->{type} eq 'hash' )
1753             {
1754             $val = CORE::delete( $self->{data}->{ $key } );
1755             }
1756            
1757 6         51 if( $locked & LOCK_EX )
1758             {
1759             $self->{_changed}++;
1760             }
1761             else
1762 6     12   27 {
1763 12         57 $self->unload( $self->{data} ) || return( $self->pass_error );
1764 12         153 }
1765             return( $val );
1766 12   33     1386 }
1767              
1768             {
1769             my $self = shift( @_ );
1770             my $key = shift( @_ );
1771 12         135 my $locked = $self->locked;
1772             unless( $locked )
1773             {
1774             $self->{data} = $self->load || return( $self->pass_error );
1775             }
1776 12     0   66 if( $self->{type} eq 'array' )
1777 0         0 {
1778 0 0       0 return( CORE::exists( $self->{data}->[ $key ] ) );
    0          
    0          
1779             }
1780 0         0 elsif( $self->{type} eq 'hash' )
1781             {
1782             return( CORE::exists( $self->{data}->{ $key } ) );
1783             }
1784 0         0 }
1785              
1786              
1787             {
1788 0         0 my $self = shift( @_ );
1789             if( caller eq __PACKAGE__ )
1790             {
1791 0 0       0 die( "I am called from within my own package\n" );
1792             }
1793 0         0 my $locked = $self->locked;
1794             my $data;
1795             if( $locked || $self->{_iterating} )
1796             {
1797 0 0       0 $data = $self->{data};
1798             $self->{_iterating} = '';
1799 0         0 }
1800             else
1801             {
1802             $data = $self->load || return( $self->pass_error );
1803             }
1804 0     0   0
1805 0         0 my $val;
1806 0         0 if( $self->{type} eq 'array' )
1807 0 0       0 {
1808             my $key = shift( @_ );
1809 0   0     0 $val = $data->[$key];
1810             }
1811 0         0 elsif( $self->{type} eq 'hash' )
1812 0 0       0 {
    0          
1813             my $key = shift( @_ );
1814 0         0 $val = $data->{ $key };
1815             }
1816             elsif( $self->{type} eq 'scalar' )
1817             {
1818 0         0 $val = $$data;
1819             }
1820             return( $val );
1821 0 0       0 }
1822              
1823 0         0 {
1824             my $self = shift( @_ );
1825             my $locked = $self->locked;
1826             unless( $locked )
1827 0 0       0 {
1828             $self->{data} = $self->load || return( $self->pass_error );
1829 0         0 }
1830             if( $self->{type} eq 'array' )
1831             {
1832             return( scalar( @{$self->{data}} ) );
1833             }
1834 0     0   0 elsif( $self->{type} eq 'hash' )
1835 0         0 {
1836 0         0 return( scalar( keys( %{$self->{data}} ) ) );
1837 0 0       0 }
1838             elsif( $self->{type} eq 'scalar' )
1839 0   0     0 {
1840             return( length( ${$self->{data}} ) );
1841 0 0       0 }
    0          
1842             }
1843 0         0  
1844             {
1845             my $self = shift( @_ );
1846             my $locked = $self->locked;
1847 0         0 unless( $locked )
1848             {
1849             $self->{data} = $self->load || return( $self->pass_error );
1850             }
1851       0     my $reset = keys( %{$self->{data}} );
1852             my $first = each( %{$self->{data}} );
1853             $self->{_iterating} = 1;
1854             return( $first );
1855 0     6   0 }
1856 6 50       7008276  
1857             {
1858 6         173 my $self = shift( @_ );
1859             my $next = each( %{$self->{data}} );
1860 0         0 if( !defined( $next ) )
1861 6         169 {
1862 6 50 33     84 $self->{_iterating} = 0;
1863             return;
1864 6         136 }
1865 0         0 else
1866             {
1867             $self->{_iterating} = 1;
1868             return( $next );
1869 0   50     0 }
1870             }
1871              
1872 6         125 {
1873 6 50       51 my $self = shift( @_ );
    50          
    50          
1874             my $locked = $self->locked;
1875 6         154 unless( $locked )
1876 0         0 {
1877             $self->{data} = $self->load || return( $self->pass_error );
1878             }
1879             my $val = pop( @{$self->{data}} );
1880 0         0 if( $locked & LOCK_EX )
1881 0         0 {
1882             $self->{_changed}++;
1883             }
1884             else
1885 0         0 {
1886             $self->unload( $self->{data} ) || return( $self->pass_error );
1887 6         35 }
1888             return( $val );
1889             }
1890              
1891             {
1892 6     0   157 my $self = shift( @_ );
1893 0         0 my $locked = $self->locked;
1894 0 0       0 unless( $locked )
1895             {
1896 0   0     0 $self->{data} = $self->load || return( $self->pass_error );
1897             }
1898 0 0       0 push( @{$self->{data}}, @_ );
    0          
    0          
1899             if( $locked & LOCK_EX )
1900 0         0 {
  0         0  
1901             $self->{_changed}++;
1902             }
1903             else
1904 0         0 {
  0         0  
1905             $self->unload( $self->{data} ) || return( $self->pass_error );
1906             }
1907             }
1908 0         0  
  0         0  
1909             {
1910             my $self = shift( @_ );
1911             my $locked = $self->locked;
1912             unless( $locked )
1913             {
1914 0     0   0 $self->{data} = $self->load || return( $self->pass_error );
1915 0         0 }
1916 0 0       0 if( $self->{type} eq 'hash' )
1917             {
1918 0   0     0 return( scalar( keys( %{$self->{data}} ) ) );
1919             }
1920 0         0 }
  0         0  
1921 0         0  
  0         0  
1922 0         0 {
1923 0         0 my $self = shift( @_ );
1924             my $locked = $self->locked;
1925             unless( $locked )
1926             {
1927             $self->{data} = $self->load || return( $self->pass_error );
1928 0     0   0 }
1929 0         0 my $val = shift( @{$self->{data}} );
  0         0  
1930 0 0       0 if( $locked & LOCK_EX )
1931             {
1932 0         0 $self->{_changed}++;
1933 0         0 }
1934             else
1935             {
1936             $self->load( $self->{data} ) || return( $self->pass_error );
1937 0         0 }
1938 0         0 return( $val );
1939             }
1940              
1941             {
1942             my $self = shift( @_ );
1943             my( $offset, $length, @vals ) = @_;
1944 0     0   0 my $locked = $self->locked;
1945 0         0 unless( $locked )
1946 0 0       0 {
1947             $self->{data} = $self->load || return( $self->pass_error );
1948 0   0     0 }
1949             my @values = splice( @{$self->{data}}, $offset, $length, @vals );
1950 0         0 if( $locked & LOCK_EX )
  0         0  
1951 0 0       0 {
1952             $self->{_changed}++;
1953 0         0 }
1954             else
1955             {
1956             $self->unload( $self->{data} ) || return( $self->pass_error );
1957 0 0       0 }
1958             return( @values );
1959 0         0 }
1960              
1961             {
1962             my $self = shift( @_ );
1963             my $locked = $self->locked;
1964 0     0   0 unless( $locked )
1965 0         0 {
1966 0 0       0 $self->{data} = $self->load || return( $self->pass_error );
1967             }
1968 0   0     0
1969             if( $self->{type} eq 'array' )
1970 0         0 {
  0         0  
1971 0 0       0 my( $key, $val ) = @_;
1972             $self->{data}->[$key] = $val;
1973 0         0 }
1974             elsif( $self->{type} eq 'hash' )
1975             {
1976             my( $key, $val ) = @_;
1977 0 0       0 $self->{data}->{ $key } = $val;
1978             }
1979             elsif( $self->{type} eq 'scalar' )
1980             {
1981             my $val = shift( @_ );
1982             $self->{data} = \$val;
1983 0     0   0 }
1984 0         0
1985 0 0       0 if( $locked & LOCK_EX )
1986             {
1987 0   0     0 $self->{_changed}++;
1988             }
1989 0 0       0 else
1990             {
1991 0         0 $self->unload( $self->{data} ) || return( $self->pass_error );
  0         0  
1992             }
1993             return( 1 );
1994             }
1995              
1996             {
1997 0     0   0 my $self = shift( @_ );
1998 0         0 my $len = shift( @_ );
1999 0 0       0 my $locked = $self->locked;
2000             unless( $locked )
2001 0   0     0 {
2002             $self->{data} = $self->load || return( $self->pass_error );
2003 0         0 }
  0         0  
2004 0 0       0 $#{$self->{data}} = $len - 1;
2005             if( $locked & LOCK_EX )
2006 0         0 {
2007             $self->{_changed}++;
2008             }
2009             else
2010 0 0       0 {
2011             $self->unload( $self->{data} ) || return( $self->pass_error );
2012 0         0 }
2013             return( $len );
2014             }
2015              
2016             {
2017 0     0   0 my $self = shift( @_ );
2018 0         0 my $locked = $self->locked;
2019 0         0 unless( $locked )
2020 0 0       0 {
2021             $self->{data} = $self->load || return( $self->pass_error );
2022 0   0     0 }
2023             my $val = unshift( @{$self->{data}}, @_ );
2024 0         0 if( $locked & LOCK_EX )
  0         0  
2025 0 0       0 {
2026             $self->{_changed}++;
2027 0         0 }
2028             else
2029             {
2030             $self->unload( $self->{data} ) || return( $self->pass_error );
2031 0 0       0 }
2032             return( $val );
2033 0         0 }
2034              
2035             {
2036             my $self = shift( @_ );
2037             }
2038 0     10   0  
2039 10         1521  
2040 10 50       102 {
2041             my $self = shift( @_ );
2042 10   50     108 my @info = caller;
2043             my $sub = [caller(1)]->[3];
2044             my $sh = $self->shared ||
2045 10 50       91 return( $self->error( "No shared memory object found." ) );
    50          
    50          
2046             my $repo = $sh->read;
2047 10         197 $repo = {} if( !defined( $repo ) || !CORE::length( $repo ) );
2048 0         0 warn( "Warning only: I was expecting an hash reference from reading the shared memory repository, but instead got '", ( $repo // '' ), "'\n" ) if( ref( $repo ) ne 'HASH' && $self->_warnings_is_enabled );
2049             my $addr = $self->addr || return( $self->error( "No variable address found!" ) );
2050             my $data = $repo->{ $addr };
2051             if( my $obj = tied( $self->{type} eq 'array' ? @$data : $self->{type} eq 'hash' ? @$data : $$data ) )
2052 0         0 {
2053 0         0 die( "Data received ($data) is tied to class '", ref( $obj ), "'!\n" );
2054             }
2055            
2056             if( !ref( $data ) )
2057 0         0 {
2058 10         73 warn( "Shared memory block with id '", $sh->id, "' -> addr '$addr' does not contain a reference: '$data' (called from package $info[0] in file $info[1] at line $info[2] from subroutine $sub)\n" );
2059             }
2060             elsif( lc( ref( $data ) ) ne $self->{type} )
2061 10 50       86 {
2062             warn( "Data retrieved from shared memory with id '", $sh->id, "' -> addr '$addr' is expected to contain a reference of type '$self->{type}', but instead contains a reference of type '", lc( ref( $data ) ), "' (called from package $info[0] in file $info[1] at line $info[2] from subroutine $sub)\n" );
2063 10         66 }
2064             return( $data );
2065             }
2066              
2067 0 50       0 {
2068             my $self = shift( @_ );
2069 10         131 my $sh = $self->shared ||
2070             return( $self->error( "No shared memory object found." ) );
2071             my $repo = $sh->read;
2072             $repo = {} if( !defined( $repo ) || !CORE::length( $repo ) );
2073             warn( "Warning only: I was expecting an hash reference from reading the shared memory repository, but instead got '", ( $repo // '' ), "'\n" ) if( ref( $repo ) ne 'HASH' && $self->_warnings_is_enabled );
2074 10     0   95 my $addr = $self->addr || return( $self->error( "No variable address found!" ) );
2075 0         0 $repo->{_lock} = {} if( !CORE::exists( $repo->{_lock} ) || ref( $repo->{_lock} ) ne 'HASH' );
2076 0         0 if( CORE::exists( $repo->{_lock}->{ $addr } ) )
2077 0 0       0 {
2078             warnings::warn( "Variable '", $self->{value}, "' with address '$self->{addr}' is already locked by process (", $repo->{_lock}->{ $addr }, "). Is it us? ", ( $repo->{_lock}->{ $addr } == $$ ? 'Yes' : 'No' ), "\n" ) if( warnings::enabled() || $DEBUG );
2079 0   0     0 return( $self );
2080             }
2081 0         0 $repo->{_lock}->{ $addr } = $$;
  0         0  
2082 0 0       0 my $rv = $sh->write( $repo );
2083             return( $self->error( "Unable to write to shared memory with shared memory object $sh: ", $sh->error ) ) if( !defined( $rv ) );
2084 0         0 return( $self );
2085             }
2086              
2087             {
2088 0 0       0 my $self = shift( @_ );
2089             my $sh = $self->shared ||
2090 0         0 return( $self->error( "No shared memory object found." ) );
2091             my $repo = $sh->read;
2092             $repo = {} if( !defined( $repo ) || !CORE::length( $repo ) );
2093             warn( "Warning only: I was expecting an hash reference from reading the shared memory repository, but instead got '", ( $repo // '' ), "'\n" ) if( ref( $repo ) ne 'HASH' && $self->_warnings_is_enabled );
2094             my $addr = $self->addr || return( $self->error( "No variable address found!" ) );
2095 0     0   0 $repo->{_lock} = {} if( !CORE::exists( $repo->{_lock} ) || ref( $repo->{_lock} ) ne 'HASH' );
2096 0         0 return( CORE::exists( $repo->{_lock}->{ $addr } ) );
2097 0 0       0 }
2098              
2099 0   0     0 {
2100             my $self = shift( @_ );
2101 0         0 my $sh = $self->shared ||
  0         0  
2102 0 0       0 return( $self->error( "No shared memory object found." ) );
2103             my $repo = $sh->read;
2104 0         0 $repo = {} if( !defined( $repo ) || !CORE::length( $repo ) );
2105             warn( "Warning only: I was expecting an hash reference from reading the shared memory repository, but instead got '", ( $repo // '' ), "'\n" ) if( ref( $repo ) ne 'HASH' && $self->_warnings_is_enabled );
2106             my $addr = $self->addr || return( $self->error( "No variable address found!" ) );
2107             CORE::delete( $repo->{ $addr } );
2108 0 0       0 $sh->lock( LOCK_EX );
2109             $sh->write( $repo ) || return( $self->pass_error( $sh->error ) );
2110 0         0 $sh->unlock;
2111             return( $self );
2112             }
2113              
2114             # sub shared { return( shift->_set_get_scalar( 'shared', @_ ) ); }
2115 0     0   0  
2116             {
2117             my $self = shift( @_ );
2118 0     42   0 my $sh = $self->shared ||
2119             return( $self->error( "No shared memory object found." ) );
2120             my $data = shift( @_ );
2121             my $addr = $self->addr || return( $self->error( "No variable address found!" ) );
2122 42     16   278 my $repo = $sh->read;
2123 16         95 $repo = {} if( !defined( $repo ) || !CORE::length( $repo ) );
2124 16         137 warn( "Warning only: I was expecting an hash reference from reading the shared memory repository, but instead got '", ( $repo // '' ), "'\n" ) if( ref( $repo ) ne 'HASH' && $self->_warnings_is_enabled );
2125 16   50     386 $repo->{ $addr } = $data;
2126             $sh->lock( LOCK_EX );
2127 16         128 my $rv = $sh->write( $repo );
2128 16 50 33     109 return( $self->error( "Unable to write to shared memory block with shared memory object $sh: ", $sh->error ) ) if( !defined( $rv ) );
2129 16 50 0     104110 $sh->unlock;
      33        
2130 16   50     159 return( $self );
2131 16         98 }
2132 16 50       117  
    50          
    50          
2133             {
2134 16         238 my $self = shift( @_ );
2135             my $sh = $self->shared ||
2136             return( $self->error( "No shared memory object found." ) );
2137 0 50       0 my $repo = $sh->read;
    50          
2138             $repo = {} if( !defined( $repo ) || !CORE::length( $repo ) );
2139 16         254 warn( "Warning only: I was expecting an hash reference from reading the shared memory repository, but instead got '", ( $repo // '' ), "'\n" ) if( ref( $repo ) ne 'HASH' && $self->_warnings_is_enabled );
2140             my $addr = $self->addr || return( $self->error( "No variable address found!" ) );
2141             if( $repo->{_lock}->{ $addr } != $$ )
2142             {
2143 0         0 return( $self->error( "Unable to remove the lock. This process ($$) is not the owner of the lock (", $repo->{_lock}->{ $addr }, ")." ) );
2144             }
2145 0         0
2146             # Credits to IPC::Shareable for the idea
2147             if( $self->{_changed} )
2148             {
2149             $repo->{ $addr } = $self->{data};
2150 16     0   165 $self->{_changed} = 0;
2151 0   0     0 }
2152             CORE::delete( $repo->{_lock}->{ $addr } );
2153 0         0 $sh->lock( LOCK_EX );
2154 0 0 0     0 my $rv = $sh->write( $repo );
2155 0 0 0     0 return( $self->error( "Unable to write to shared memory with shared memory object $sh: ", $sh->error ) ) if( !defined( $rv ) );
      0        
2156 0   0     0 $sh->unlock;
2157 0 0 0     0 return( $self );
2158 0 0       0 }
2159              
2160 0 0 0     0 {
    0          
2161 0         0 my $class = shift( @_ );
2162             my $opts = $class->_get_args_as_hash( @_ );
2163 0         0 return( $class->error( "No shared memory object provided." ) ) if( !CORE::exists( $opts->{shm} ) || !CORE::length( $opts->{shm} ) || !Scalar::Util::blessed( $opts->{shm} ) );
2164 0         0 return( $class->error( "No data type was provided for shared memory tie." ) ) if( !CORE::length( $opts->{type} ) || !CORE::length( $opts->{type} ) );
2165 0 0       0 return( $class->error( "Data type '$opts->{type}' is unsupported." ) ) if( $opts->{type} !~ /^(array|hash|scalar)$/i );
2166 0         0 # if( !CORE::length( $opts->{type} ) && CORE::length( $opts->{value} ) )
2167             # {
2168             # return( $class->error( "Value provided ($opts->{value}) is not a reference!" ) ) if( !ref( $opts->{value} ) );
2169             # $opts->{type} = ref( $opts->{value} );
2170             # }
2171 0     16   0 $opts->{type} = lc( $opts->{type} );
2172 16   50     105 my $hash =
2173             {
2174 16         201 # addr => Scalar::Util::refaddr( $opts->{value} ),
2175 16 50 33     183 addr => $opts->{addr},
2176 16 50 0     113200 debug => ( $opts->{debug} // 0 ),
      33        
2177 16   50     189 shared => $opts->{shm},
2178 16 50 33     182 type => $opts->{type},
2179 16         198 };
2180             my $self = bless( $hash => ( ref( $class ) || $class ) );
2181            
2182             if( $opts->{type} eq 'scalar' )
2183             {
2184 16     0   176 $self->{data} = \'';
2185 0   0     0 }
2186             elsif( $opts->{type} eq 'array' )
2187 0         0 {
2188 0 0 0     0 $self->{data} = [];
2189 0 0 0     0 }
      0        
2190 0   0     0 elsif( $opts->{type} eq 'hash' )
2191 0         0 {
2192 0         0 $self->{data} = {};
2193 0 0       0 }
2194 0         0 return( $self );
2195 0         0 }
2196              
2197             {
2198             my $self = shift( @_ );
2199 0     50   0 my @info = caller();
2200             print( STDERR __PACKAGE__, "::DESTROY: called from package '$info[0]' in file '$info[1]' at line $info[2]\n" ) if( $DEBUG );
2201             };
2202              
2203 50     10   569 {
2204 10   50     93 my $self = shift( @_ );
2205             my $serialiser = shift( @_ ) // '';
2206 10         70 my $class = ref( $self ) || $self;
2207 10   50     60 my %hash = %$self;
2208 10         62 if( $self->{type} eq 'scalar' )
2209 10 50 33     71 {
2210 10 50 0     64417 my $str = ${$self->{data}};
      33        
2211 10         152 $hash{data} = \$str;
2212 10         47 }
2213 10         112 elsif( $self->{type} eq 'array' )
2214 10 50       7014 {
2215 10         26602 my @ref = @{$self->{data}};
2216 10         128 $hash{data} = \@ref;
2217             }
2218             elsif( $self->{type} eq 'hash' )
2219             {
2220             my %ref = %{$self->{data}};
2221 10     0   3490 $hash{data} = \%ref;
2222 0   0     0 }
2223             return( [$class, \%hash] ) if( $serialiser eq 'Sereal' && Sereal::Encoder->VERSION <= version->parse( '4.023' ) );
2224 0         0 return( $class, \%hash );
2225 0 0 0     0 }
2226 0 0 0     0  
      0        
2227 0   0     0  
2228 0 0       0  
2229             {
2230 0         0 my( $self, undef, @args ) = @_;
2231             my $ref = ( CORE::scalar( @args ) == 1 && CORE::ref( $args[0] ) eq 'ARRAY' ) ? CORE::shift( @args ) : \@args;
2232             my $class = ( CORE::defined( $ref ) && CORE::ref( $ref ) eq 'ARRAY' && CORE::scalar( @$ref ) > 1 ) ? CORE::shift( @$ref ) : ( CORE::ref( $self ) || $self );
2233             my $hash = CORE::ref( $ref ) eq 'ARRAY' ? CORE::shift( @$ref ) : {};
2234 0 0       0 my $new;
2235             # Storable pattern requires to modify the object it created rather than returning a new one
2236 0         0 if( CORE::ref( $self ) )
2237 0         0 {
2238             foreach( CORE::keys( %$hash ) )
2239 0         0 {
2240 0         0 $self->{ $_ } = CORE::delete( $hash->{ $_ } );
2241 0         0 }
2242 0 0       0 $new = $self;
2243 0         0 }
2244 0         0 else
2245             {
2246             $new = CORE::bless( $hash => $class );
2247             }
2248             CORE::return( $new );
2249 0     24   0 }
2250 24         117  
2251 24 50 33     120 1;
      33        
2252 24 50 33     2460 # NOTE: POD
2253 24 50       180  
2254             =encoding utf-8
2255              
2256             =head1 NAME
2257              
2258             Promise::Me - Fork Based Promise with Asynchronous Execution, Async, Await and Shared Data
2259 24         195  
2260             =head1 SYNOPSIS
2261              
2262             use Promise::Me; # exports async, await and share
2263             my $p = Promise::Me->new(sub
2264             {
2265             # $_ is available as an array reference containing
2266             # $_->[0] the code reference to the resolve method
2267 24   50     87 # $_->[1] the code reference to the reject method
2268 24   33     237 # Some regular code here
2269             })->then(sub
2270 24 100       216 {
    100          
    50          
2271             my $res = shift( @_ ); # return value from the code executed above
2272 24         117 # more processing...
2273             })->then(sub
2274             {
2275             my $more = shift( @_ ); # return value from the previous then
2276 12         837 # more processing...
2277             })->catch(sub
2278             {
2279             my $exception = shift( @_ ); # error that occured is caught here
2280 6         69 })->finally(sub
2281             {
2282 6         45 # final processing
2283             })->then(sub
2284             {
2285             # A last then may be added after finally
2286             };
2287 24     6   162  
2288 6         23 # You can share data among processes for all systems, including Windows
2289 6 50       31 my $data : shared = {};
2290             my( $name, %attributes, @options );
2291             share( $name, %attributes, @options );
2292              
2293             my $p1 = Promise::Me->new( $code_ref )->then(sub
2294 6     0   164 {
2295 0   0       my $res = shift( @_ );
2296 0   0       # more processing...
2297 0           })->catch(sub
2298 0 0         {
    0          
    0          
2299             my $err = shift( @_ );
2300 0           # Do something with the exception
  0            
2301 0           });
2302              
2303             my $p2 = Promise::Me->new( $code_ref )->then(sub
2304             {
2305 0           my $res = shift( @_ );
  0            
2306 0           # more processing...
2307             })->catch(sub
2308             {
2309             my $err = shift( @_ );
2310 0           # Do something with the exception
  0            
2311 0           });
2312              
2313 0 0 0       my @results = await( $p1, $p2 );
2314 0            
2315             # Wait for all promise to resolve. If one is rejected, this super promise is rejected
2316             my @results = Promise::Me->all( $p1, $p2 );
2317 0     0      
2318             # First promise that is resolved or rejected makes this super promise resolved and
2319 0     0     # return the result
2320             my @results = Promise::Me->race( $p1, $p2 );
2321              
2322             # Automatically turns this subroutine into one that runs asynchronously and returns
2323 0     0     # a promise
2324 0 0 0       async sub fetch_remote
2325 0 0 0       {
      0        
2326 0 0         # Do some http request that will run asynchronously thanks to 'async'
2327 0           }
2328              
2329 0 0         sub do_something
2330             {
2331 0           # some code here
2332             my $p = Promise::Me->new(sub
2333 0           {
2334             # some work that needs to run asynchronously
2335 0           })->then(sub
2336             {
2337             # More processing here
2338             })->catch(sub
2339 0           {
2340             # Oops something went wrong
2341 0           my $exception = shift( @_ );
2342             });
2343             # No need for this subroutine 'do_something' to be prefixed with 'async'.
2344             # This is not JavaScript you know
2345             await $p;
2346             }
2347              
2348             sub do_something
2349             {
2350             # some code here
2351             my $p = Promise::Me->new(sub
2352             {
2353             # some work that needs to run asynchronously
2354             })->then(sub
2355             {
2356             # More processing here
2357             })->catch(sub
2358             {
2359             # Oops something went wrong
2360             my $exception = shift( @_ );
2361             })->wait;
2362             # Always returns a reference
2363             my $result = $p->result;
2364             }
2365              
2366             =head1 VERSION
2367              
2368             v0.4.4
2369              
2370             =head1 DESCRIPTION
2371              
2372             L<Promise::Me> is an implementation of the JavaScript promise using fork for asynchronous tasks. Fork is great, because it is well supported by all operating systems (L<except AmigaOS, RISC OS and VMS|perlport>) and effectively allows for asynchronous execution.
2373              
2374             While JavaScript has asynchronous execution at its core, which means that two consecutive lines of code will execute simultaneously, under perl, those two lines would be executed one after the other. For example:
2375              
2376             # Assuming the function getRemote makes an http query of a remote resource that takes time
2377             let response = getRemote('https://example.com/api');
2378             console.log(response);
2379              
2380             Under JavaScript, this would yield: C<undefined>, but in perl
2381              
2382             my $resp = $ua->get('https://example.com/api');
2383             say( $resp );
2384              
2385             Would correctly return the response object, but it will hang until it gets the returned object whereas in JavaScript, it would not wait.
2386              
2387             In JavaScript, because of this asynchronous execution, before people were using callback hooks, which resulted in "callback from hell", i.e. something like this[1]:
2388              
2389             getData(function(x){
2390             getMoreData(x, function(y){
2391             getMoreData(y, function(z){
2392             ...
2393             });
2394             });
2395             });
2396              
2397             [1] Taken from this L<StackOverflow discussion|https://stackoverflow.com/questions/25098066/what-is-callback-hell-and-how-and-why-does-rx-solve-it>
2398              
2399             And then, they came up with L<Promise|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>, so that instead of wrapping your code in a callback function you get instead a promise object that gets called when certain events get triggered, like so[2]:
2400              
2401             const myPromise = new Promise((resolve, reject) => {
2402             setTimeout(() => {
2403             resolve('foo');
2404             }, 300);
2405             });
2406              
2407             myPromise
2408             .then(handleResolvedA, handleRejectedA)
2409             .then(handleResolvedB, handleRejectedB)
2410             .then(handleResolvedC, handleRejectedC);
2411              
2412             [2] Taken from L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>
2413              
2414             Chaining is easy to implement in perl and L<Promise::Me> does it too. Where it gets more tricky is returning a promise immediately without waiting for further execution, i.e. a deferred promise, like the following in JavaScript:
2415              
2416             function getRemote(url)
2417             {
2418             let promise = new Promise((resolve, reject) =>
2419             {
2420             setTimeout(() => reject(new Error("Whoops!")), 1000);
2421             });
2422             // Maybe do some other stuff here
2423             return( promise );
2424             }
2425              
2426             In this example, under JavaScript, the C<promise> will be returned immediately. However, under perl, the equivalent code would be executed sequentially. For example, using the excellent module L<Promise::ES6>:
2427              
2428             sub get_remote
2429             {
2430             my $url = shift( @_ );
2431             my $p = Promise::ES6->new(sub($res)
2432             {
2433             $res->( Promise::ES6->resolve(123) );
2434             });
2435             # Do some more work that would take some time
2436             return( $p );
2437             }
2438              
2439             In the example above, the promise C<$p> would not be returned until all the tasks are completed before the C<return> statement, contrary to JavaScript where it would be returned immediately.
2440              
2441             So, in perl people have started to use loop such as L<AnyEvent> or L<IO::Async> with "conditional variable" to get that asynchronous execution, but you need to use loops. For example (taken from L<Promise::AsyncAwait>):
2442              
2443             use Promise::AsyncAwait;
2444             use Promise::XS;
2445              
2446             sub delay {
2447             my $secs = shift;
2448              
2449             my $d = Promise::XS::deferred();
2450              
2451             my $timer; $timer = AnyEvent->timer(
2452             after => $secs,
2453             cb => sub {
2454             undef $timer;
2455             $d->resolve($secs);
2456             },
2457             );
2458              
2459             return $d->promise();
2460             }
2461              
2462             async sub wait_plus_1 {
2463             my $num = await delay(0.01);
2464              
2465             return 1 + $num;
2466             }
2467              
2468             my $cv = AnyEvent->condvar();
2469             wait_plus_1()->then($cv, sub { $cv->croak(@_) });
2470              
2471             my ($got) = $cv->recv();
2472              
2473             So, in the midst of this, I have tried to provide something without event loop by using fork instead as exemplified in the L</SYNOPSIS>
2474              
2475             For a framework to do asynchronous tasks, you might also be interested in L<Coro>, from L<Marc A. Lehmann|https://metacpan.org/author/MLEHMANN> original author of L<AnyEvent> event loop.
2476              
2477             =head1 METHODS
2478              
2479             =head2 new
2480              
2481             my $p = Promise::Me->new(sub
2482             {
2483             # $_ is available as an array reference containing
2484             # $_->[0] the code reference to the resolve method
2485             # $_->[1] the code reference to the reject method
2486             my( $resolve, $reject ) = @$_;
2487             # some code to run asynchronously
2488             $resolve->();
2489             # or
2490             $reject->();
2491             # or maybe just
2492             die( "Something\n" ); # will be trapped by catch()
2493             });
2494              
2495             # or
2496             my $p = Promise::Me->new(sub
2497             {
2498             # some code to run asynchronously
2499             }, { debug => 4, result_shared_mem_size => 2097152, shared_vars_mem_size => 65536, timeout => 2, medium => 'mmap' });
2500              
2501             Instantiate a new C<Promise::Me> object.
2502              
2503             It takes a code reference such as an anonymous subroutine or a reference to a subroutine, and optionally an hash reference of options.
2504              
2505             The variable C<$_> is available and contains an array reference containing a code reference for C<$resolve> and C<$reject>. Thus if you wanted the execution fo your code to be resolved and calling L</then>, you could either return some return values, or explicitly call the code reference C<< $resolve->() >>. Likewise if you want to force the promise to be rejected so it call the next chained L</catch>, you can explicitly call C<< $reject->() >>. This is similar in spirit to what JavaScript Promise does.
2506              
2507             Also, if you return an exception object, whose class you have set with the I<exception_class> option, L<Promise::Me> will be able to detect it and call L</reject> accordingly and pass it the exception object as its sole argument.
2508              
2509             You can also die with a an exception object (see L<perlfunc/die>) and it will be caught by L<Promise::Me> and the exception object will be passed to L</reject> calling the next chained L</catch> method.
2510              
2511             The options supported are:
2512              
2513             =over 4
2514              
2515             =item I<debug> integer
2516              
2517             Sets the debug level. This can be quite verbose and will slow down the process, so use with caution.
2518              
2519             =item I<exception_class>
2520              
2521             The exception class you want to use, so that L<Promise::Me> can properly detect it when it is return from the main callback and call L</reject>, passing the exception object as it sole parameter.
2522              
2523             =item I<medium>
2524              
2525             This sets the medium type to use to share data between parent and child process. Possible values are: C<memory>, C<mmap> or C<file>
2526              
2527             It defaults to the class variable C<$SHARE_MEDIUM>
2528              
2529             See also the related method L</medium>
2530              
2531             =item I<result_shared_mem_size> integer
2532              
2533             Sets the shared memory segment to store the asynchronous process results. This default to the value of the global variable C<$RESULT_MEMORY_SIZE>, which is by default 512K bytes, or if empty or not defined, the value of the constant C<Module::Generic::SharedMemXS::SHM_BUFSIZ>, which is 64K bytes.
2534              
2535             =item serialiser
2536              
2537             String. Specify the serialiser to use for L<Promise::Me>. Possible values are: L<cbor|CBOR::XS>, L<sereal|Sereal> or L<storable|Storable::Improved>
2538              
2539             By default, the value is set to the global variable C<$SERIALISER>, which defaults to C<storable>
2540              
2541             This value is passed to L<Module::Generic::File::Mmap>, L<Module::Generic::File::Cache>, or L<Module::Generic::SharedMemXS> depending on your choice of shared memory medium.
2542              
2543             =item I<shared_vars_mem_size> integer
2544              
2545             Sets the shared memory segment to store the shared variable data, i.e. the ones declared with L</shared>. This defaults to the value of the global variable C<$SHARED_MEMORY_SIZE>, which is by default 64K bytes, or if empty or not defined, the value of the constant C<Module::Generic::SharedMemXS::SHM_BUFSIZ>, which is 64K bytes.
2546              
2547             =item I<timeout> integer
2548              
2549             Currently unused.
2550              
2551             =item I<use_cache_file>
2552              
2553             Boolean. If true, L<Promise::Me> will use a cache file instead of shared memory block. If you are on system that do not support shared memory, L<Promise::Me> will automatically revert to L<Module::Generic::File::Cache> to handle data shared among processes.
2554              
2555             You can use the global package variable C<$SHARE_MEDIUM> to set the default value for all object instantiation.
2556              
2557             C<$SHARE_MEDIUM> value can be either C<memory> for shared memory, C<mmap> for cache mmap or C<file> for shared cache file.
2558              
2559             =item I<use_mmap>
2560              
2561             Boolean. If true, L<Promise::Me> will use a cache mmap file with L<Module::Generic::File::Mmap> instead of a shared memory block. However, please note that you need to have installed L<Cache::FastMmap> in order to use this.
2562              
2563             You can use the global package variable C<$SHARE_MEDIUM> to set the default value for all object instantiation.
2564              
2565             C<$SHARE_MEDIUM> value can be either C<memory> for shared memory, C<mmap> for cache mmap or C<file> for shared cache file.
2566              
2567             =back
2568              
2569             =head2 catch
2570              
2571             This takes a code reference as its unique argument and is added to the chain of handlers.
2572              
2573             It will be called upon an exception being met or if L</reject> is called.
2574              
2575             The callback subroutine will be passed the error object as its unique argument.
2576              
2577             Be careful not to intentionally die in the C<catch> block unless you have another C<catch> block after, because if you die, it will trigger another catch, and you will not see that you died in the first place, because, well, it was caught... Instead you want to get the exception and log it, print it, do something with it.
2578              
2579             =head2 medium
2580              
2581             Sets or gets the medium type to be used to share data between parent and child process. Valid values are: C<memory>, C<mmap> and C<file>
2582              
2583             =head2 reject
2584              
2585             This takes one or more arguments that will be passed to the next L</catch> handler, if any.
2586              
2587             It will mark the promise as C<rejected> and will go no further in the chain.
2588              
2589             =head2 rejected
2590              
2591             Takes a boolean value and sets or gets the C<rejected> status of the promise.
2592              
2593             This is typically set by L</reject> and you should not call this directly, but use instead L</reject>.
2594              
2595             =head2 resolve
2596              
2597             This takes one or more arguments that will be passed to the next L</then> handler, if any.
2598              
2599             It will mark the promise as C<resolved> and will the next L</then> handler.
2600              
2601             =head2 resolved
2602              
2603             Takes a boolean value and sets or gets the C<resolved> status of the promise.
2604              
2605             This is typically set by L</resolve> and you should not call this directly, but use instead L</resolve>.
2606              
2607             =head2 result
2608              
2609             This sets or gets the result returned by the asynchronous process. The data is exchanged through shared memory.
2610              
2611             This method is used internally in combination with L</await>, L</all> and L</race>
2612              
2613             The value returned is always a reference, such as array, hash or scalar reference.
2614              
2615             If the asynchronous process returns a simple string for example, C<result> will be an array reference containing that string.
2616              
2617             Thus, unless the value returned is 1 element and it is a reference, it will be made of an array reference.
2618              
2619             =head2 serialiser
2620              
2621             String. Sets or gets the serialiser to use for L<Promise::Me>. Possible values are: L<cbor|CBOR::XS>, L<sereal|Sereal> or L<storable|Storable::Improved>
2622              
2623             By default, the value is set to the global variable C<$SERIALISER>, which defaults to C<storable>
2624              
2625             =head2 then
2626              
2627             This takes a code reference as its unique argument and is added to the chain of handlers.
2628              
2629             It will be called upon resolution of the promise or when L</resolve> is called.
2630              
2631             The callback subroutine is passed as arguments whatever the previous callback returned.
2632              
2633             =head2 timeout
2634              
2635             Sets gets a timeout. This is currently not used. There is no timeout for the asynchronous process.
2636              
2637             If you want to set a timeout, you can use L</wait>, or L</await>
2638              
2639             =head2 wait
2640              
2641             This is a chain method whose purpose is to indicate that we must wait for the asynchronous process to complete.
2642              
2643             Promise::Me->new(sub
2644             {
2645             # Some operation to be run asynchronously
2646             })->then(sub
2647             {
2648             # Do some processing of the result
2649             })->catch(sub
2650             {
2651             # Cath any exceptions
2652             })->wait;
2653              
2654             =head1 CLASS FUNCTIONS
2655              
2656             =head2 all
2657              
2658             Provided with one or more C<Promise::Me> objects, and this will wait for all of them to be resolved.
2659              
2660             It returns an array equal in size to the number of promises provided initially.
2661              
2662             However, if one promise is rejected, L</all> stops and returns it immediately.
2663              
2664             my @results = Promise::Me->all( $p1, $p2, $p3 );
2665              
2666             Contrary to its JavaScript equivalent, you do not need to pass an array reference of promises, although you could.
2667              
2668             # Works too, but not mandatory
2669             my @results = Promise::Me->all( [ $p1, $p2, $p3 ] );
2670              
2671             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all> for more information.
2672              
2673             =head2 race
2674              
2675             Provided with one or more C<Promise::Me> objects, and this will return the result of the first promise that resolves or is rejected.
2676              
2677             Contrary to its JavaScript equivalent, you do not need to pass an array reference of promises, although you could.
2678              
2679             # Works too, but not mandatory
2680             my @results = Promise::Me->race( [ $p1, $p2, $p3 ] );
2681              
2682             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race> for more information.
2683              
2684             =head1 EXPORTED FUNCTIONS
2685              
2686             =head2 async
2687              
2688             This is a static function exported by default and that wrap the subroutine thus prefixed into one that returns a promise and return its code asynchronously.
2689              
2690             For example:
2691              
2692             async sub fetch
2693             {
2694             my $ua = LWP::UserAgent->new;
2695             my $res = $ua->get( 'https://example.com' );
2696             }
2697              
2698             This would be equivalent to:
2699              
2700             Promise::Me->new(sub
2701             {
2702             my $ua = LWP::UserAgent->new;
2703             my $res = $ua->get( 'https://example.com' );
2704             });
2705              
2706             Of course, since, in our example above, C<fetch> would return a promise, you could chain L</then>, L</catch> and L</finally>, such as:
2707              
2708             async sub fetch
2709             {
2710             my $ua = LWP::UserAgent->new;
2711             my $res = $ua->get( 'https://example.com' );
2712             }->then(sub
2713             {
2714             my $res = shift( @_ );
2715             if( !$resp->is_success )
2716             {
2717             die( My::Exception->new( "Unable to fetch remote content." ) );
2718             }
2719             })->catch(sub
2720             {
2721             my $exception = shift( @_ );
2722             $logger->warn( $exception );
2723             })->finally(sub
2724             {
2725             $dbi->disconnect;
2726             });
2727              
2728             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function> for more information on C<async>
2729              
2730             =head2 await
2731              
2732             Provided with one or more promises and L</await> will wait until each one of them is completed and return an array of their result with one entry per promise. Each promise result is a reference (array, hash, or scalar, or object for example)
2733              
2734             my @results = await( $p1, $p2, $p3 );
2735              
2736             =head2 lock
2737              
2738             This locks a shared variable.
2739              
2740             my $data : shared = {};
2741             lock( $data );
2742             $data->{location} = 'Tokyo';
2743             unlock( $data );
2744              
2745             See L</"SHARED VARIABLES"> for more information about shared variables.
2746              
2747             =head2 share
2748              
2749             Provided with one or more variables and this will enable them to be shared with the asynchronous processes.
2750              
2751             Currently supported variable types are: array, hash and scalar (string) reference.
2752              
2753             my( $name, @first_names, %preferences );
2754             share( $name, @first_names, %preferences );
2755             $name = 'Momo Taro';
2756              
2757             Promise::Me->new(sub
2758             {
2759             $preferences{name} = $name = 'Mr. ' . $name;
2760             print( "Hello $name\n" );
2761             $preferences{location} = 'Okayama';
2762             $preferences{lang} = 'ja_JP';
2763             $preferences{locale} = '桃太郎'; # Momo Taro
2764             my $rv = $tbl->insert( \%$preferences )->exec || die( My::Exception->new( $tbl->error ) );
2765             $rv;
2766             })->then(sub
2767             {
2768             my $mail = My::Mailer->new(
2769             to => $preferences{email},
2770             name => $preferences{name},
2771             body => $welcome_ja_file,
2772             );
2773             $mail->send || die( $mail->error );
2774             })->catch(sub
2775             {
2776             my $exception = shift( @_ );
2777             $logger->write( $exception );
2778             })->finally(sub
2779             {
2780             $dbh->disconnect;
2781             });
2782              
2783             It will try to use shared memory or shared cache file depending on the value of the global package variable C<$SHARE_MEDIUM>, which can be either C<file> for L<Module::Generic::File::Cache>, C<mmap> for L<Module::Generic::File::Mmap> or C<memory> for L<Module::Generic::File::SharedMem>
2784              
2785             The value of C<$SHARED_MEMORY_SIZE>, and C<$SERIALISER> will be passed when instantiating objects for those shared memory medium.
2786              
2787             =head2 unlock
2788              
2789             This unlocks a shared variable. It has no effect on variable that have not already been shared.
2790              
2791             See L</"SHARED VARIABLES"> for more information about shared variables.
2792              
2793             =head2 unshare
2794              
2795             Unshare a variable. It has no effect on variable that have not already been shared.
2796              
2797             This should only be called before the promise is created.
2798              
2799             =head1 INTERNAL METHODS
2800              
2801             =head2 add_final_handler
2802              
2803             This is called each time a L</finally> method is called and will add to the chain the code reference provided.
2804              
2805             =head2 add_reject_handler
2806              
2807             This is called each time a L</catch> method is called and will add to the chain the code reference provided.
2808              
2809             =head2 add_resolve_handler
2810              
2811             This is called each time a L</then> method is called and will add to the chain the code reference provided.
2812              
2813             =head2 args
2814              
2815             This method is called upon promise object instantiation when initially called by L</async>.
2816              
2817             It is used to capture arguments so they can be passed to the code executed asynchronously.
2818              
2819             =head2 exec
2820              
2821             This method is called at the end of the chain. It will prepare shared variable for the child process, launch a child process using L<perlfunc/fork> and will call the next L</then> handler if the code executed successfully, or L</reject> if there was an error.
2822              
2823             =head2 exit_bit
2824              
2825             This corresponds to C<$?>. After the child process exited, L</_set_exit_values> is called and sets the value for this.
2826              
2827             =head2 exit_signal
2828              
2829             This corresponds to the integer value of the signal, if any, used to interrupt the asynchronous process.
2830              
2831             =head2 exit_status
2832              
2833             This is the integer value of the exit for the asynchronous process. If a process exited normally, this value should be 0.
2834              
2835             =head2 filter
2836              
2837             This is called by the C<import> method to filter the code using perl filter with XS module L<Filter::Util::Call> and enables data sharing, and implementation of async subroutine prefix. It relies on XS module L<PPI> for parsing perl code.
2838              
2839             =head2 get_finally_handler
2840              
2841             This is called when all chaining is complete to get the L</finally> handler, if any.
2842              
2843             =head2 get_next_by_type
2844              
2845             Get the next handler by type, i.e. C<then>, C<catch> or C<finally>
2846              
2847             =head2 get_next_reject_handler
2848              
2849             This is called to get the next L</catch> handler when a promise has been rejected, such as when an error has occurred.
2850              
2851             =head2 get_next_resolve_handler
2852              
2853             This is called to get the next L</then> handler and execute its code passing it the return value from previous block in the chain.
2854              
2855             =head2 has_coredump
2856              
2857             Returns true if the asynchronous process last exited with a core dump, false otherwise.
2858              
2859             =head2 is_child
2860              
2861             Returns true if we are called from within the asynchronous process.
2862              
2863             =head2 is_parent
2864              
2865             Returns true if we are called from within the main parent process.
2866              
2867             =head2 no_more_chaining
2868              
2869             This is set to true automatically when the end of the method chain has been reached.
2870              
2871             =head2 pid
2872              
2873             Returns the pid of the asynchronous process.
2874              
2875             =head2 share_auto_destroy
2876              
2877             This is a promise instantiation option. When set to true, the shared variables will be automatically removed from memory upon end of the main process.
2878              
2879             This is true by default. If you want to set it to false, you can do:
2880              
2881             Promise::Me->new(sub
2882             {
2883             # some code here
2884             }, {share_auto_destroy => 0})->then(sub
2885             {
2886             # some more work here, etc.
2887             });
2888              
2889             =head2 shared_mem
2890              
2891             This returns the object used for sharing data and result between the main parent process and the asynchronous child process. It can be L<Module::Generic::SharedMemXS>, L<Module::Generic::File::Mmap> or L<Module::Generic::File::Cache> depending on the value of C<$SHARE_MEDIUM>, which can be set to, respectively, C<memory>, C<mmap> or C<file>
2892              
2893             =head2 shared_space_destroy
2894              
2895             Boolean. Default to true. If true, the shared space used by the parent and child processes will be destroy automatically. Disable this if you want to debug or take a sneak peek into the data. The shared space will be either shared memory of cache file depending on the value of C<$SHARE_MEDIUM>
2896              
2897             =head2 use_async
2898              
2899             This is a boolean value which is set automatically when a promise is instantiated from L</async>.
2900              
2901             It enables subroutine arguments to be passed to the code being run asynchronously.
2902              
2903             =head1 PRIVATE METHODS
2904              
2905             =head2 _browse
2906              
2907             Used for debugging purpose only, this will print out the L<PPI> structure of the code filtered and parsed.
2908              
2909             =head2 _parse
2910              
2911             After the code has been collected, this method will quickly parse it and make changes to enable L</async>
2912              
2913             =head2 _reject_resolve
2914              
2915             This is a common code called by either L</resolve> or L</reject>
2916              
2917             =head2 _set_exit_values
2918              
2919             This is called upon the exit of the asynchronous process to set some general value about how the process exited.
2920              
2921             See L</exit_bit>, L</exit_signal> and L</exit_status>
2922              
2923             =head2 _set_shared_space
2924              
2925             This is called in L</exec> to share data including result between main parent process and asynchronous process.
2926              
2927             =head1 SHARED VARIABLES
2928              
2929             It is important to be able to share variables between processes in a seamless way.
2930              
2931             When the asynchronous process is executed, the main process first fork and from this point on all data is being duplicated in an impermeable way so that if a variable is modified, it would have no effect on its alter ego in the other process; thus the need for shareable variables.
2932              
2933             You can enable shared variables in two ways:
2934              
2935             =over 4
2936              
2937             =item 1. declaring the variable as shared
2938              
2939             my $name : shared;
2940             # Initiate a value
2941             my $location : shared = 'Tokyo';
2942             # you can also use 'pshared'
2943             my $favorite_programming_language : pshared = 'perl';
2944             # You can share array, hash and scalar
2945             my %preferences : shared;
2946             my @names : shared;
2947              
2948             =item 2. calling L</share>
2949              
2950             my( $name, %prefs, @middle_names );
2951             share( $name, %prefs, @middle_names );
2952              
2953             =back
2954              
2955             Once shared, you can use those variables normally and their values will be shared between the parent process and the asynchronous process.
2956              
2957             For example:
2958              
2959             my( $name, @first_names, %preferences );
2960             share( $name, @first_names, %preferences );
2961             $name = 'Momo Taro';
2962              
2963             Promise::Me->new(sub
2964             {
2965             $preferences{name} = $name = 'Mr. ' . $name;
2966             print( "Hello $name\n" );
2967             $preferences{location} = 'Okayama';
2968             $preferences{lang} = 'ja_JP';
2969             $preferences{locale} = '桃太郎';
2970             my $rv = $tbl->insert( \%$preferences )->exec || die( My::Exception->new( $tbl->error ) );
2971             $rv;
2972             })->then(sub
2973             {
2974             my $mail = My::Mailer->new(
2975             to => $preferences{email},
2976             name => $preferences{name},
2977             body => $welcome_ja_file,
2978             );
2979             $mail->send || die( $mail->error );
2980             })->catch(sub
2981             {
2982             my $exception = shift( @_ );
2983             $logger->write( $exception );
2984             })->finally(sub
2985             {
2986             $dbh->disconnect;
2987             });
2988              
2989             If you want to mix this feature and the usage of threads' C<shared> feature, use the keyword C<pshared> instead of C<shared>, such as:
2990              
2991             my $name : pshared;
2992              
2993             Otherwise the two keywords would conflict.
2994              
2995             =head1 SHARED MEMORY
2996              
2997             This module uses shared memory using L<Module::Generic::SharedMemXS>, or shared cache file using L<Module::Generic::File::Cache> if shared memory is not supported, or if the value of the global package variable C<$SHARE_MEDIUM> is set to C<file> instead of C<memory>. Alternatively you can also have L<Promise::Me> use cache mmap file by setting C<$SHARE_MEDIUM> to C<mmap>. This will have it use L<Module::Generic::File::Mmap>, but note that you will need to install L<Cache::FastMmap>
2998              
2999             The value of C<$SHARE_MEDIUM> is automatically initialised to C<memory> if the system, on which this module runs, supports L<IPC::SysV>, or C<mmap> if you have L<Cache::FastMmap> installed, or else to C<file>
3000              
3001             Shared memory is used for:
3002              
3003             =over 4
3004              
3005             =item 1. shared variables
3006              
3007             =item 2. storing results returned by asynchronous processes
3008              
3009             =back
3010              
3011             You can control how much shared memory is allocated for each by:
3012              
3013             =over 4
3014              
3015             =item 1. setting the global variable C<$SHARED_MEMORY_SIZE>, which default to 64K bytes.
3016              
3017             =item 2. setting the option I<result_shared_mem_size> when instantiating a new C<Promise::Me> object. If not set, this will default to L<Module::Generic::SharedMemXS::SHM_BUFSIZ> constant value which is 64K bytes.
3018              
3019             If you use L<shared cache file|Module::Generic::File::Cache>, then not setting a size is ok. It will use the space on the filesystem as needed and obviously return an error if there is no space left.
3020              
3021             You can alternatively use L<Module::Generic::File::Mmap>, which has an API similar to L<Module::Generic::File::Cache>, but uses an mmap file instead of a simple cache file and rely on the XS module L<Cache::FastMmap>, and thus is faster.
3022              
3023             =back
3024              
3025             =head1 CONCURRENCY
3026              
3027             Because L<Promise::Me> forks a separate process to run the code provided in the promise, two promises can run simultaneously. Let's take the following example:
3028              
3029             use Time::HiRes;
3030             my $result : shared = '';
3031             my $p1 = Promise::Me->new(sub
3032             {
3033             sleep(1);
3034             $result .= "Peter ";
3035             })->then(sub
3036             {
3037             print( "Promise 1: result is now: '$result'\n" );
3038             });
3039              
3040             my $p2 = Promise::Me->new(sub
3041             {
3042             sleep(0.5);
3043             $result .= "John ";
3044             })->then(sub
3045             {
3046             print( "Promise 2: result is now: '$result'\n" );
3047             });
3048             await( $p1, $p2 );
3049             print( "Result is: '$result'\n" );
3050              
3051             This will yield:
3052              
3053             Promise 2: result is now: 'John '
3054             Promise 1: result is now: 'John Peter '
3055             Result is: 'John Peter '
3056              
3057             =head1 CLASS VARIABLE
3058              
3059             =head2 $RESULT_MEMORY_SIZE
3060              
3061             This is the size in bytes of the shared memory block used for sharing result between sub process and main process, such as when you call:
3062              
3063             my $res = $prom->result;
3064              
3065             It defaults to 512Kb
3066              
3067             =head2 $SERIALISER
3068              
3069             A string representing the serialiser to use by default. A serialiser is used to serialiser data to share them between processes. This defaults to C<storable>
3070              
3071             Currently supported serialisers are: L<CBOR::XS>, L<Sereal> and L<Storable|Storable::Improved>
3072              
3073             You can set accordingly the value for C<$SERIALISER> to: C<cbor>, C<sereal> or C<storable>
3074              
3075             You can override this global value when you instantiate a new L<Promise::Me> object with the C<serialiser> option. See L</new>
3076              
3077             Note that the serialiser used to serialise shared variable, is set only via this class variable C<$SERIALISER>
3078              
3079             =head2 $SHARE_MEDIUM
3080              
3081             The value of C<$SHARE_MEDIUM> is automatically initialised to C<memory> if the system, on which this module runs, supports L<IPC::SysV>, or C<mmap> if you have L<Cache::FastMmap> installed, or else to C<file>
3082              
3083             =head2 $SHARED_MEMORY_SIZE
3084              
3085             This is the size in bytes of the shared memory block used for sharing variables between the main process and the sub processes. This is used when you share variables, such as:
3086              
3087             my $name : shared;
3088             my( $name, %prefs, @middle_names );
3089             share( $name, %prefs, @middle_names );
3090              
3091             See L</"SHARED VARIABLES">
3092              
3093             =head1 SERIALISATION
3094              
3095             L<Promise::Me> uses the following supported serialiser to serialise shared data across processes:
3096              
3097             =over 4
3098              
3099             =item * L<CBOR|CBOR::XS>
3100              
3101             =item * L<Sereal>
3102              
3103             =item * L<Storable|Storable::Improved>
3104              
3105             =back
3106              
3107             You can set which one to use globally by setting the class variable C<$SERIALISER> to C<cbor>, C<sereal> or to C<storable>
3108              
3109             You can also set which serialiser to use on a per promise object by setting the option C<serialiser>. See L</new>
3110              
3111             =head1 AUTHOR
3112              
3113             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
3114              
3115             =head1 SEE ALSO
3116              
3117             L<Promise::XS>, L<Promise::E6>, L<Promise::AsyncAwait>, L<AnyEvent::XSPromises>, L<Async>, L<Promises>, L<Mojo::Promise>
3118              
3119             L<Mozilla documentation on promises|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises>
3120              
3121             =head1 COPYRIGHT & LICENSE
3122              
3123             Copyright(c) 2021-2022 DEGUEST Pte. Ltd. DEGUEST Pte. Ltd.
3124              
3125             All rights reserved
3126              
3127             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
3128              
3129             =cut