File Coverage

blib/lib/Promise/Me.pm
Criterion Covered Total %
statement 754 1479 50.9
branch 244 1022 23.8
condition 147 606 24.2
subroutine 106 150 70.6
pod 49 52 94.2
total 1300 3309 39.2


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