File Coverage

blib/lib/Promise/Me.pm
Criterion Covered Total %
statement 756 1483 50.9
branch 246 1028 23.9
condition 147 606 24.2
subroutine 106 150 70.6
pod 49 52 94.2
total 1304 3319 39.2


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