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