line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Async::Defer; |
2
|
|
|
|
|
|
|
|
3
|
18
|
|
|
18
|
|
490766
|
use 5.012; |
|
18
|
|
|
|
|
82
|
|
|
18
|
|
|
|
|
792
|
|
4
|
18
|
|
|
18
|
|
104
|
use warnings; |
|
18
|
|
|
|
|
38
|
|
|
18
|
|
|
|
|
496
|
|
5
|
18
|
|
|
18
|
|
102
|
use strict; |
|
18
|
|
|
|
|
34
|
|
|
18
|
|
|
|
|
649
|
|
6
|
18
|
|
|
18
|
|
96
|
use Carp; |
|
18
|
|
|
|
|
45
|
|
|
18
|
|
|
|
|
1919
|
|
7
|
|
|
|
|
|
|
|
8
|
18
|
|
|
18
|
|
18712
|
use version; our $VERSION = qv('0.9.5'); # REMINDER: update Changes |
|
18
|
|
|
|
|
49560
|
|
|
18
|
|
|
|
|
114
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# REMINDER: update dependencies in Makefile.PL |
11
|
18
|
|
|
18
|
|
1867
|
use Scalar::Util qw( refaddr ); |
|
18
|
|
|
|
|
38
|
|
|
18
|
|
|
|
|
2594
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
## no critic (ProhibitBuiltinHomonyms) |
14
|
|
|
|
|
|
|
|
15
|
18
|
|
|
18
|
|
106
|
use constant NOT_RUNNING=> -1; |
|
18
|
|
|
|
|
32
|
|
|
18
|
|
|
|
|
1278
|
|
16
|
18
|
|
|
18
|
|
103
|
use constant OP_CODE => 1; |
|
18
|
|
|
|
|
53
|
|
|
18
|
|
|
|
|
762
|
|
17
|
18
|
|
|
18
|
|
86
|
use constant OP_DEFER => 2; |
|
18
|
|
|
|
|
56
|
|
|
18
|
|
|
|
|
790
|
|
18
|
18
|
|
|
18
|
|
303
|
use constant OP_IF => 3; |
|
18
|
|
|
|
|
30
|
|
|
18
|
|
|
|
|
810
|
|
19
|
18
|
|
|
18
|
|
83
|
use constant OP_ELSE => 4; |
|
18
|
|
|
|
|
41
|
|
|
18
|
|
|
|
|
1325
|
|
20
|
18
|
|
|
18
|
|
122
|
use constant OP_ENDIF => 5; |
|
18
|
|
|
|
|
39
|
|
|
18
|
|
|
|
|
815
|
|
21
|
18
|
|
|
18
|
|
83
|
use constant OP_WHILE => 6; |
|
18
|
|
|
|
|
51
|
|
|
18
|
|
|
|
|
821
|
|
22
|
18
|
|
|
18
|
|
96
|
use constant OP_ENDWHILE=> 7; |
|
18
|
|
|
|
|
34
|
|
|
18
|
|
|
|
|
857
|
|
23
|
18
|
|
|
18
|
|
83
|
use constant OP_TRY => 8; |
|
18
|
|
|
|
|
30
|
|
|
18
|
|
|
|
|
897
|
|
24
|
18
|
|
|
18
|
|
80
|
use constant OP_CATCH => 9; |
|
18
|
|
|
|
|
31
|
|
|
18
|
|
|
|
|
714
|
|
25
|
18
|
|
|
18
|
|
79
|
use constant OP_FINALLY => 10; |
|
18
|
|
|
|
|
29
|
|
|
18
|
|
|
|
|
697
|
|
26
|
18
|
|
|
18
|
|
118
|
use constant OP_ENDTRY => 11; |
|
18
|
|
|
|
|
39
|
|
|
18
|
|
|
|
|
100901
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my %SELF; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub new { |
32
|
195
|
|
|
195
|
1
|
1007655
|
my ($class) = @_; |
33
|
195
|
|
|
|
|
507
|
my $this = bless {}, $class; |
34
|
195
|
|
|
|
|
1651
|
$SELF{refaddr $this} = { |
35
|
|
|
|
|
|
|
parent => undef, # parent Defer object, if any |
36
|
|
|
|
|
|
|
opcode => [], # [[OP_CODE,$sub], [OP_TRY], ...] |
37
|
|
|
|
|
|
|
pc => NOT_RUNNING,# point to _CURRENT_ opcode, if any |
38
|
|
|
|
|
|
|
iter => [], # [[1,$outer_while_pc], [8,$inner_while_pc], ...] |
39
|
|
|
|
|
|
|
findone => undef, # undef or ['continue'] or ['break'] or ['throw',$err] |
40
|
|
|
|
|
|
|
}; |
41
|
195
|
|
|
|
|
467
|
return $this; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub DESTROY { |
45
|
62
|
|
|
62
|
|
496322
|
my ($this) = @_; |
46
|
62
|
|
|
|
|
742
|
delete $SELF{refaddr $this}; |
47
|
62
|
|
|
|
|
1547
|
return; |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
sub clone { |
51
|
7
|
|
|
7
|
1
|
21
|
my ($this) = @_; |
52
|
7
|
|
|
|
|
22
|
my $self = $SELF{refaddr $this}; |
53
|
|
|
|
|
|
|
|
54
|
7
|
|
|
|
|
17
|
my $clone = __PACKAGE__->new(); |
55
|
7
|
|
|
|
|
24
|
my $clone_self = $SELF{refaddr $clone}; |
56
|
|
|
|
|
|
|
|
57
|
7
|
|
|
|
|
8
|
$clone_self->{opcode} = [ @{ $self->{opcode} } ]; |
|
7
|
|
|
|
|
19
|
|
58
|
7
|
|
|
|
|
12
|
%{$clone} = %{$this}; |
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
23
|
|
59
|
7
|
|
|
|
|
20
|
return $clone; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub iter { |
63
|
203
|
|
|
203
|
1
|
666
|
my ($this) = @_; |
64
|
203
|
|
|
|
|
433
|
my $self = $SELF{refaddr $this}; |
65
|
|
|
|
|
|
|
|
66
|
203
|
100
|
|
|
|
183
|
if (!@{ $self->{iter} }) { |
|
203
|
|
|
|
|
465
|
|
67
|
5
|
|
|
|
|
98
|
croak 'iter() can be used only inside while'; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
198
|
|
|
|
|
849
|
return $self->{iter}[-1][0]; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub _add { |
74
|
706
|
|
|
706
|
|
1174
|
my ($this, $op, @params) = @_; |
75
|
706
|
|
|
|
|
1838
|
my $self = $SELF{refaddr $this}; |
76
|
|
|
|
|
|
|
|
77
|
706
|
100
|
|
|
|
1672
|
if ($self->{pc} != NOT_RUNNING) { |
78
|
8
|
|
|
|
|
131
|
croak 'unable to modify while running'; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
698
|
|
|
|
|
761
|
push @{ $self->{opcode} }, [ $op, @params ]; |
|
698
|
|
|
|
|
1806
|
|
82
|
698
|
|
|
|
|
2002
|
return $this; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub do { |
86
|
297
|
|
|
297
|
1
|
5986
|
my ($this, $task, @more_tasks) = @_; |
87
|
297
|
100
|
|
|
|
683
|
if(@more_tasks){ |
88
|
3
|
|
|
|
|
8
|
for ($task, @more_tasks) { |
89
|
15
|
|
|
|
|
30
|
$this->do($_); |
90
|
|
|
|
|
|
|
} |
91
|
3
|
|
|
|
|
11
|
return $this; |
92
|
|
|
|
|
|
|
} |
93
|
294
|
|
|
|
|
505
|
given (ref $task) { |
94
|
294
|
|
|
|
|
611
|
when ('CODE') { |
95
|
198
|
|
|
|
|
414
|
return $this->_add(OP_CODE, $task); |
96
|
|
|
|
|
|
|
} |
97
|
96
|
|
|
|
|
150
|
when (__PACKAGE__) { |
98
|
76
|
|
|
|
|
159
|
return $this->_add(OP_DEFER, $task); |
99
|
|
|
|
|
|
|
} |
100
|
20
|
|
|
|
|
35
|
when ('ARRAY') { |
101
|
10
|
|
|
|
|
20
|
my %task = map { $_ => $task->[$_] } 0 .. $#{ $task }; |
|
23
|
|
|
|
|
65
|
|
|
10
|
|
|
|
|
26
|
|
102
|
10
|
|
|
|
|
47
|
return $this->_add(OP_CODE, _do_batch(1, %task)); |
103
|
|
|
|
|
|
|
} |
104
|
10
|
|
|
|
|
23
|
when ('HASH') { |
105
|
9
|
|
|
|
|
14
|
return $this->_add(OP_CODE, _do_batch(0, %{ $task })); |
|
9
|
|
|
|
|
35
|
|
106
|
|
|
|
|
|
|
} |
107
|
1
|
|
|
|
|
1
|
default { |
108
|
1
|
|
|
|
|
25
|
croak 'require CODE/Defer object or ARRAY/HASH in first param' |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub _do_batch { |
114
|
19
|
|
|
19
|
|
50
|
my ($is_array, %task) = @_; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# Isolate each task in own Defer object to guarantee they won't be |
117
|
|
|
|
|
|
|
# surprised by shared state. |
118
|
19
|
|
|
|
|
50
|
for my $key (keys %task) { |
119
|
43
|
|
|
|
|
53
|
my $task; |
120
|
43
|
|
|
|
|
77
|
given (ref $task{$key}) { |
121
|
43
|
|
|
|
|
72
|
when ('CODE') { |
122
|
39
|
|
|
|
|
77
|
$task = __PACKAGE__->new(); |
123
|
39
|
|
|
|
|
99
|
$task->do( $task{$key} ); |
124
|
|
|
|
|
|
|
} |
125
|
4
|
|
|
|
|
9
|
when (__PACKAGE__) { |
126
|
4
|
|
|
|
|
15
|
$task = $task{$key}->clone(); |
127
|
|
|
|
|
|
|
} |
128
|
0
|
|
|
|
|
0
|
default { |
129
|
0
|
0
|
|
|
|
0
|
my $pos = $is_array ? $key+1 : "{$key}"; |
130
|
0
|
|
|
|
|
0
|
croak 'require CODE/Defer object in param '.$pos; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
} |
133
|
43
|
|
|
|
|
98
|
$task{$key} = $task; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
return sub{ |
137
|
19
|
|
|
19
|
|
45
|
my ($d, @taskparams) = @_; |
138
|
14
|
|
|
|
|
38
|
my %taskparams |
139
|
|
|
|
|
|
|
= !$is_array ? (@taskparams) |
140
|
19
|
100
|
|
|
|
65
|
: (map { ($_ => $taskparams[$_]) } 0 .. $#taskparams); |
141
|
|
|
|
|
|
|
|
142
|
19
|
100
|
|
|
|
53
|
if (!keys %task) { |
143
|
2
|
|
|
|
|
6
|
return $d->done(); |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
17
|
|
|
|
|
38
|
my %taskresults = map { $_ => undef } keys %task; |
|
43
|
|
|
|
|
111
|
|
147
|
17
|
|
|
|
|
86
|
for my $key (sort keys %task) { # sort just to simplify testing |
148
|
43
|
|
|
|
|
106
|
my $t = __PACKAGE__->new(); |
149
|
43
|
|
|
|
|
92
|
$t->try(); |
150
|
43
|
|
|
|
|
109
|
$t->do( $task{$key} ); |
151
|
|
|
|
|
|
|
$t->catch( |
152
|
|
|
|
|
|
|
qr/.*/ms => sub{ |
153
|
2
|
|
|
|
|
5
|
my ($t,$err) = @_; ## no critic (ProhibitReusedNames) |
154
|
2
|
|
|
|
|
5
|
$t->{err} = $err; |
155
|
2
|
|
|
|
|
7
|
$t->done(); |
156
|
|
|
|
|
|
|
}, |
157
|
|
|
|
|
|
|
FINALLY => sub{ |
158
|
19
|
|
|
|
|
35
|
my ($t, @result) = @_; ## no critic (ProhibitReusedNames) |
159
|
19
|
|
100
|
|
|
235
|
$taskresults{$key} = $t->{err} // \@result; |
160
|
19
|
100
|
|
|
|
45
|
if (!grep {!defined} values %taskresults) { |
|
77
|
|
|
|
|
158
|
|
161
|
|
|
|
|
|
|
my @taskresults |
162
|
11
|
|
|
|
|
31
|
= !$is_array ? %taskresults |
163
|
5
|
100
|
|
|
|
27
|
: map { $taskresults{$_-1} } 1 .. keys %taskresults; |
164
|
5
|
|
|
|
|
18
|
$d->done(@taskresults); |
165
|
|
|
|
|
|
|
} |
166
|
19
|
|
|
|
|
80
|
return $t->done(); |
167
|
|
|
|
|
|
|
}, |
168
|
43
|
|
|
|
|
459
|
); |
169
|
43
|
100
|
|
|
|
57
|
$t->run( undef, @{ $taskparams{$key} || [] } ); |
|
43
|
|
|
|
|
178
|
|
170
|
|
|
|
|
|
|
} |
171
|
19
|
|
|
|
|
175
|
}; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub if { |
175
|
23
|
|
|
23
|
1
|
1788
|
my ($this, $code) = @_; |
176
|
23
|
100
|
66
|
|
|
139
|
if (!$code || ref $code ne 'CODE') { |
177
|
1
|
|
|
|
|
25
|
croak 'require CODE in first param'; |
178
|
|
|
|
|
|
|
} |
179
|
22
|
|
|
|
|
49
|
return $this->_add(OP_IF, $code); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub else { |
183
|
15
|
|
|
15
|
1
|
1303
|
my ($this) = @_; |
184
|
15
|
|
|
|
|
32
|
return $this->_add(OP_ELSE); |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub end_if { |
188
|
22
|
|
|
22
|
1
|
1219
|
my ($this) = @_; |
189
|
22
|
|
|
|
|
61
|
return $this->_add(OP_ENDIF); |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub while { |
193
|
21
|
|
|
21
|
1
|
1265
|
my ($this, $code) = @_; |
194
|
21
|
100
|
66
|
|
|
154
|
if (!$code || ref $code ne 'CODE') { |
195
|
1
|
|
|
|
|
25
|
croak 'require CODE in first param'; |
196
|
|
|
|
|
|
|
} |
197
|
20
|
|
|
|
|
123
|
return $this->_add(OP_WHILE, $code); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub end_while { |
201
|
19
|
|
|
19
|
1
|
925
|
my ($this) = @_; |
202
|
19
|
|
|
|
|
52
|
return $this->_add(OP_ENDWHILE); |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub try { |
206
|
87
|
|
|
87
|
1
|
999
|
my ($this) = @_; |
207
|
87
|
|
|
|
|
193
|
return $this->_add(OP_TRY); |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub catch { |
211
|
91
|
|
|
91
|
1
|
4512
|
my ($this, @param) = @_; |
212
|
91
|
100
|
|
|
|
341
|
if (2 > @param) { |
|
|
100
|
|
|
|
|
|
213
|
2
|
|
|
|
|
31
|
croak 'require at least 2 params'; |
214
|
|
|
|
|
|
|
} elsif (@param % 2) { |
215
|
1
|
|
|
|
|
8
|
croak 'require even number of params'; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
88
|
|
|
|
|
123
|
my ($finally, @catch); |
219
|
88
|
|
|
|
|
278
|
while (my ($cond, $code) = splice @param, 0, 2) { |
220
|
146
|
100
|
|
|
|
284
|
if ($cond eq 'FINALLY') { |
221
|
62
|
|
66
|
|
|
358
|
$finally ||= $code; |
222
|
|
|
|
|
|
|
} else { |
223
|
84
|
|
|
|
|
295
|
push @catch, $cond, $code; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
88
|
100
|
|
|
|
190
|
if (@catch) { |
228
|
80
|
|
|
|
|
176
|
$this->_add(OP_CATCH, @catch); |
229
|
|
|
|
|
|
|
} |
230
|
87
|
100
|
|
|
|
194
|
if ($finally) { |
231
|
61
|
|
|
|
|
132
|
$this->_add(OP_FINALLY, $finally); |
232
|
|
|
|
|
|
|
} |
233
|
87
|
|
|
|
|
250
|
return $this->_add(OP_ENDTRY); |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub _check_stack { |
237
|
244
|
|
|
244
|
|
302
|
my ($self) = @_; |
238
|
244
|
|
|
|
|
257
|
my @stack; |
239
|
244
|
|
|
|
|
803
|
my %op_open = ( |
240
|
|
|
|
|
|
|
OP_IF() => 'end_if()', |
241
|
|
|
|
|
|
|
OP_WHILE() => 'end_while()', |
242
|
|
|
|
|
|
|
OP_TRY() => 'catch()', |
243
|
|
|
|
|
|
|
); |
244
|
244
|
|
|
|
|
1261
|
my %op_close = ( |
245
|
|
|
|
|
|
|
OP_ENDIF() => [ OP_IF, 'end_if()' ], |
246
|
|
|
|
|
|
|
OP_ENDWHILE() => [ OP_WHILE, 'end_while()' ], |
247
|
|
|
|
|
|
|
OP_ENDTRY() => [ OP_TRY, 'catch()' ], |
248
|
|
|
|
|
|
|
); |
249
|
244
|
|
|
|
|
395
|
my $extra = 0; |
250
|
244
|
|
|
|
|
388
|
for (my $i = 0; $i < @{ $self->{opcode} }; $i++) { |
|
1259
|
|
|
|
|
3089
|
|
251
|
1023
|
|
|
|
|
2605
|
my ($op) = @{ $self->{opcode}[ $i ] }; |
|
1023
|
|
|
|
|
1667
|
|
252
|
|
|
|
|
|
|
|
253
|
1023
|
100
|
100
|
|
|
3742
|
if ($op == OP_CATCH || $op == OP_FINALLY) { |
254
|
188
|
|
|
|
|
200
|
$extra++; |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
1023
|
100
|
|
|
|
3364
|
if ($op_open{$op}) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
258
|
198
|
|
|
|
|
903
|
push @stack, [$op,0]; # second number is counter for seen OP_ELSE |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
elsif ($op_close{$op}) { |
261
|
192
|
|
|
|
|
189
|
my ($close_op, $close_func) = @{ $op_close{$op} }; |
|
192
|
|
|
|
|
304
|
|
262
|
192
|
100
|
100
|
|
|
994
|
if (@stack && $stack[-1][0] == $close_op) { |
263
|
186
|
|
|
|
|
405
|
pop @stack; |
264
|
|
|
|
|
|
|
} else { |
265
|
6
|
|
|
|
|
92
|
croak 'unexpected '.$close_func.' at operation '.($i+1-$extra); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
elsif ($op == OP_ELSE) { |
269
|
32
|
100
|
66
|
|
|
153
|
if (!(@stack && $stack[-1][0] == OP_IF)) { |
|
|
100
|
|
|
|
|
|
270
|
1
|
|
|
|
|
12
|
croak 'unexpected else() at operation '.($i+1-$extra); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
elsif ($stack[-1][1]) { |
273
|
1
|
|
|
|
|
13
|
croak 'unexpected double else() at operation '.($i+1-$extra); |
274
|
|
|
|
|
|
|
} |
275
|
30
|
|
|
|
|
45
|
$stack[-1][1]++; |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
} |
278
|
236
|
100
|
|
|
|
493
|
if (@stack) { |
279
|
5
|
|
|
|
|
59
|
croak 'expected '.$op_open{ $stack[-1][0] }.' at end'; |
280
|
|
|
|
|
|
|
} |
281
|
231
|
|
|
|
|
1087
|
return; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub run { |
285
|
247
|
|
|
247
|
1
|
48184
|
my ($this, $d, @result) = @_; |
286
|
247
|
|
|
|
|
634
|
my $self = $SELF{refaddr $this}; |
287
|
|
|
|
|
|
|
|
288
|
247
|
|
|
|
|
508
|
my %op_stmt = map {$_=>1} OP_CODE, OP_DEFER, OP_FINALLY; |
|
741
|
|
|
|
|
1843
|
|
289
|
247
|
100
|
|
|
|
414
|
if (!grep {$op_stmt{ $_->[0] }} @{ $self->{opcode} }) { |
|
1036
|
|
|
|
|
2140
|
|
|
247
|
|
|
|
|
1292
|
|
290
|
2
|
|
|
|
|
24
|
croak 'no operations to run, use do() first'; |
291
|
|
|
|
|
|
|
} |
292
|
245
|
100
|
|
|
|
621
|
if ($self->{pc} != NOT_RUNNING) { |
293
|
1
|
|
|
|
|
16
|
croak 'already running'; |
294
|
|
|
|
|
|
|
} |
295
|
244
|
|
|
|
|
454
|
_check_stack($self); |
296
|
|
|
|
|
|
|
|
297
|
231
|
100
|
|
|
|
503
|
if(ref($d) eq 'CODE') { |
298
|
1
|
|
|
|
|
1
|
my $callback = $d; |
299
|
1
|
|
|
|
|
4
|
$d = __PACKAGE__->new(); |
300
|
|
|
|
|
|
|
$d->do( |
301
|
|
|
|
|
|
|
sub { |
302
|
1
|
|
|
1
|
|
2
|
my ($defer, @results) = @_; |
303
|
1
|
|
|
|
|
3
|
$callback->(@results); |
304
|
1
|
|
|
|
|
6
|
$defer->done; |
305
|
|
|
|
|
|
|
} |
306
|
1
|
|
|
|
|
5
|
); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
231
|
|
|
|
|
324
|
$self->{parent} = $d; |
310
|
231
|
|
|
|
|
694
|
$this->done(@result); |
311
|
228
|
|
|
|
|
984
|
return; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub _op { |
315
|
2201
|
|
|
2201
|
|
2970
|
my ($self) = @_; |
316
|
2201
|
|
|
|
|
2186
|
my ($op, @params) = @{ $self->{opcode}[ $self->{pc} ] }; |
|
2201
|
|
|
|
|
4881
|
|
317
|
2201
|
100
|
|
|
|
6708
|
return wantarray ? ($op, @params) : $op; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub done { |
321
|
733
|
|
|
733
|
1
|
30424
|
my ($this, @result) = @_; |
322
|
733
|
|
|
|
|
1713
|
my $self = $SELF{refaddr $this}; |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# If OP_FINALLY was called while processing continue(), break() or throw(), |
325
|
|
|
|
|
|
|
# and it has finished with done() - continue with continue/break/throw by |
326
|
|
|
|
|
|
|
# calling them _again_ instead of done(). |
327
|
733
|
100
|
|
|
|
1989
|
if ($self->{findone}) { |
328
|
23
|
|
|
|
|
22
|
my ($method, @param) = @{ $self->{findone} }; |
|
23
|
|
|
|
|
72
|
|
329
|
23
|
|
|
|
|
101
|
return $this->$method(@param); |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
710
|
|
|
|
|
1139
|
while (++$self->{pc} <= $#{ $self->{opcode} }) { |
|
1082
|
|
|
|
|
2943
|
|
333
|
921
|
|
|
|
|
1626
|
my ($opcode, @param) = _op($self); |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# @result received from previous opcode will be available to next |
336
|
|
|
|
|
|
|
# opcode only if these opcodes stay one-after-one without any |
337
|
|
|
|
|
|
|
# other opcodes between them (like OP_IF, for example). |
338
|
|
|
|
|
|
|
# Only exception is (no-op) OP_TRY, OP_CATCH and OP_ENDTRY. |
339
|
|
|
|
|
|
|
# This limitation should help user to avoid subtle bugs. |
340
|
921
|
|
|
|
|
1371
|
given ($opcode) { |
341
|
921
|
|
|
|
|
1574
|
when (OP_CODE) { |
342
|
361
|
|
|
|
|
1196
|
return $param[0]->($this, @result); |
343
|
|
|
|
|
|
|
} |
344
|
560
|
|
|
|
|
786
|
when (OP_DEFER) { |
345
|
76
|
|
|
|
|
269
|
return $param[0]->run($this, @result); |
346
|
|
|
|
|
|
|
} |
347
|
484
|
|
|
|
|
546
|
when (OP_FINALLY) { |
348
|
40
|
|
|
|
|
276
|
return $param[0]->($this, @result); |
349
|
|
|
|
|
|
|
} |
350
|
444
|
|
|
|
|
1175
|
when ([OP_TRY,OP_CATCH,OP_ENDTRY]) { |
351
|
215
|
|
|
|
|
1157
|
next; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
} |
354
|
229
|
|
|
|
|
372
|
@result = (); |
355
|
|
|
|
|
|
|
|
356
|
229
|
|
|
|
|
246
|
given ($opcode) { |
357
|
229
|
|
|
|
|
241
|
when (OP_IF) { |
358
|
|
|
|
|
|
|
# true - do nothing (i.e. just move to next opcode) |
359
|
|
|
|
|
|
|
# false - skip to nearest OP_ELSE or OP_ENDIF |
360
|
52
|
100
|
|
|
|
108
|
if (!$param[0]->( $this )) { |
361
|
28
|
|
|
|
|
77
|
my $stack = 0; |
362
|
28
|
|
|
|
|
40
|
while (++$self->{pc} <= $#{ $self->{opcode} }) { |
|
117
|
|
|
|
|
241
|
|
363
|
117
|
|
|
|
|
175
|
my $op = _op($self); |
364
|
117
|
100
|
100
|
|
|
557
|
$op == OP_ELSE && !$stack ? last |
|
|
100
|
100
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
365
|
|
|
|
|
|
|
: $op == OP_ENDIF && !$stack ? last |
366
|
|
|
|
|
|
|
: $op == OP_IF ? $stack++ |
367
|
|
|
|
|
|
|
: $op == OP_ENDIF ? $stack-- |
368
|
|
|
|
|
|
|
: next; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
} |
372
|
177
|
|
|
|
|
234
|
when (OP_ELSE) { |
373
|
|
|
|
|
|
|
# skip this OP_ELSE branch to nearest OP_ENDIF |
374
|
11
|
|
|
|
|
13
|
my $stack = 0; |
375
|
11
|
|
|
|
|
14
|
while (++$self->{pc} <= $#{ $self->{opcode} }) { |
|
34
|
|
|
|
|
69
|
|
376
|
34
|
|
|
|
|
56
|
my $op = _op($self); |
377
|
34
|
100
|
100
|
|
|
134
|
$op == OP_ENDIF && !$stack ? last |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
378
|
|
|
|
|
|
|
: $op == OP_IF ? $stack++ |
379
|
|
|
|
|
|
|
: $op == OP_ENDIF ? $stack-- |
380
|
|
|
|
|
|
|
: next; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
} |
383
|
166
|
|
|
|
|
190
|
when (OP_WHILE) { |
384
|
|
|
|
|
|
|
# We can "enter" OP_WHILE in two cases - for the first time, |
385
|
|
|
|
|
|
|
# OR because of continue() called inside this OP_WHILE. |
386
|
98
|
100
|
100
|
|
|
127
|
if (!@{$self->{iter}} || $self->{iter}[-1][1] != $self->{pc}) { |
|
98
|
|
|
|
|
490
|
|
387
|
33
|
|
|
|
|
62
|
push @{ $self->{iter} }, [ 1, $self->{pc} ]; |
|
33
|
|
|
|
|
87
|
|
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
# We now already "inside" this OP_WHILE, so we can use break() |
390
|
|
|
|
|
|
|
# to exit _this_ OP_WHILE. |
391
|
98
|
100
|
|
|
|
300
|
if (!$param[0]->( $this )) { |
392
|
23
|
|
|
|
|
114
|
return $this->break(); |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
} |
395
|
68
|
|
|
|
|
103
|
when (OP_ENDWHILE) { |
396
|
|
|
|
|
|
|
# We now still "inside" current OP_WHILE, so we can use continue() |
397
|
|
|
|
|
|
|
# to repeat _this_ OP_WHILE. |
398
|
49
|
|
|
|
|
180
|
return $this->continue(); |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
161
|
|
|
|
|
247
|
$self->{pc} = NOT_RUNNING; |
404
|
161
|
100
|
|
|
|
1407
|
if ($self->{parent}) { |
405
|
53
|
|
|
|
|
636
|
return $self->{parent}->done(@result); |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
# If we're here, done() was called by last opcode, and this is |
409
|
|
|
|
|
|
|
# top-level Defer object, nothing more to do - STOP. |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# Before executing continue/break logic we have to find and execute all |
413
|
|
|
|
|
|
|
# OP_FINALLY for all already open OP_TRY blocks within this OP_WHILE. |
414
|
|
|
|
|
|
|
# So, this helper skip opcodes inside this OP_WHILE until it found |
415
|
|
|
|
|
|
|
# either OP_FINALLY or OP_ENDWHILE or last opcode. |
416
|
|
|
|
|
|
|
sub _skip_while { |
417
|
132
|
|
|
132
|
|
158
|
my ($self) = @_; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# 1. continue() can be called exactly on OP_ENDWHILE (by done()) |
420
|
|
|
|
|
|
|
# 2. continue/break can be called by last opcode |
421
|
|
|
|
|
|
|
# In both cases we shouldn't do anything (including moving {pc} forward). |
422
|
132
|
100
|
66
|
|
|
198
|
if (_op($self) == OP_ENDWHILE || $self->{pc} == $#{ $self->{opcode} }) { |
|
83
|
|
|
|
|
282
|
|
423
|
49
|
|
|
|
|
69
|
return; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
83
|
|
|
|
|
130
|
my $stack = 0; |
427
|
83
|
|
|
|
|
82
|
my $trystack = 0; |
428
|
83
|
|
|
|
|
111
|
while (++$self->{pc} < $#{ $self->{opcode} }) { |
|
333
|
|
|
|
|
861
|
|
429
|
294
|
|
|
|
|
444
|
my $op = _op($self); |
430
|
294
|
100
|
100
|
|
|
1855
|
$op == OP_ENDWHILE && !$stack ? last |
|
|
100
|
100
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
431
|
|
|
|
|
|
|
: $op == OP_WHILE ? $stack++ |
432
|
|
|
|
|
|
|
: $op == OP_ENDWHILE ? $stack-- |
433
|
|
|
|
|
|
|
: $op == OP_TRY ? $trystack++ |
434
|
|
|
|
|
|
|
: $op == OP_ENDTRY && $trystack ? $trystack-- |
435
|
|
|
|
|
|
|
: $op == OP_FINALLY && !$trystack ? last |
436
|
|
|
|
|
|
|
: next; |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
83
|
|
|
|
|
118
|
return; |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub continue { |
443
|
88
|
|
|
88
|
1
|
181
|
my ($this) = @_; |
444
|
88
|
|
|
|
|
195
|
my $self = $SELF{refaddr $this}; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# Any next call to continue/break/throw cancels current continue/break/throw (if any). |
447
|
88
|
|
|
|
|
115
|
$self->{findone} = undef; |
448
|
|
|
|
|
|
|
|
449
|
88
|
|
|
|
|
157
|
_skip_while($self); |
450
|
88
|
|
|
|
|
145
|
my ($op, @param) = _op($self); |
451
|
88
|
100
|
|
|
|
188
|
if ($op == OP_FINALLY) { |
452
|
|
|
|
|
|
|
# If OP_FINALLY ends with done() - call continue() again instead. |
453
|
21
|
|
|
|
|
40
|
$self->{findone} = ['continue']; |
454
|
21
|
|
|
|
|
58
|
return $param[0]->($this); |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# We now at OP_ENDWHILE, rewind to corresponding OP_WHILE. |
458
|
67
|
|
|
|
|
81
|
my $stack = 0; |
459
|
67
|
|
|
|
|
206
|
while (--$self->{pc} > 0) { |
460
|
444
|
|
|
|
|
634
|
$op = _op($self); |
461
|
444
|
100
|
100
|
|
|
1917
|
$op == OP_WHILE && !$stack ? last |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
462
|
|
|
|
|
|
|
: $op == OP_ENDWHILE ? $stack++ |
463
|
|
|
|
|
|
|
: $op == OP_WHILE ? $stack-- |
464
|
|
|
|
|
|
|
: next; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
# If continue was called outside OP_WHILE there is no iteration number. |
468
|
67
|
100
|
|
|
|
67
|
if (@{ $self->{iter} }) { |
|
67
|
|
|
|
|
183
|
|
469
|
65
|
|
|
|
|
108
|
$self->{iter}[-1][0]++; |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# Step one opcode back because done() will move one opcode forward |
473
|
|
|
|
|
|
|
# and so process this OP_WHILE. |
474
|
67
|
|
|
|
|
90
|
--$self->{pc}; |
475
|
67
|
|
|
|
|
227
|
return $this->done(); |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub break { |
479
|
44
|
|
|
44
|
1
|
103
|
my ($this) = @_; |
480
|
44
|
|
|
|
|
138
|
my $self = $SELF{refaddr $this}; |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
# Any next call to continue/break/throw cancels current continue/break/throw (if any). |
483
|
44
|
|
|
|
|
64
|
$self->{findone} = undef; |
484
|
|
|
|
|
|
|
|
485
|
44
|
|
|
|
|
110
|
_skip_while($self); |
486
|
44
|
|
|
|
|
78
|
my ($op, @param) = _op($self); |
487
|
44
|
100
|
|
|
|
110
|
if ($op == OP_FINALLY) { |
488
|
|
|
|
|
|
|
# If OP_FINALLY ends with done() - call break() again instead. |
489
|
13
|
|
|
|
|
26
|
$self->{findone} = ['break']; |
490
|
13
|
|
|
|
|
35
|
return $param[0]->($this); |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# We now at OP_ENDWHILE. |
494
|
31
|
|
|
|
|
34
|
pop @{ $self->{iter} }; |
|
31
|
|
|
|
|
52
|
|
495
|
31
|
|
|
|
|
120
|
return $this->done(); |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
sub throw { |
499
|
58
|
|
|
58
|
1
|
259
|
my ($this, $err) = @_; |
500
|
58
|
|
|
|
|
147
|
my $self = $SELF{refaddr $this}; |
501
|
58
|
|
100
|
|
|
194
|
$err //= q{}; |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# Any next call to continue/break/throw cancels current continue/break/throw (if any). |
504
|
58
|
|
|
|
|
87
|
$self->{findone} = undef; |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
# If throw() was called by break opcode in this OP_TRY (either OP_FINALLY, |
507
|
|
|
|
|
|
|
# or OP_CATCH if there no OP_FINALLY in this OP_TRY), then we should look |
508
|
|
|
|
|
|
|
# for handler in outer OP_TRY, not in this one. |
509
|
|
|
|
|
|
|
# So we set $stack=1 to skip over current OP_TRY's OP_ENDTRY. |
510
|
58
|
100
|
|
|
|
73
|
my ($nextop) = @{ $self->{opcode}[ $self->{pc} + 1 ] || [] }; |
|
58
|
|
|
|
|
204
|
|
511
|
58
|
100
|
100
|
|
|
245
|
my $stack = $nextop && $nextop == OP_ENDTRY ? 1 : 0; |
512
|
|
|
|
|
|
|
# Skip until OP_CATCH or OP_FINALLY in current OP_TRY block. |
513
|
|
|
|
|
|
|
# If while skipping we exit some OP_WHILE(s) - pop their iterators. |
514
|
58
|
|
|
|
|
147
|
while (++$self->{pc} <= $#{ $self->{opcode} }) { |
|
81
|
|
|
|
|
282
|
|
515
|
75
|
|
|
|
|
132
|
my $op = _op($self); |
516
|
0
|
|
|
|
|
0
|
$op == OP_CATCH && !$stack ? last |
517
|
|
|
|
|
|
|
: $op == OP_FINALLY && !$stack ? last |
518
|
|
|
|
|
|
|
: $op == OP_TRY ? $stack++ |
519
|
|
|
|
|
|
|
: $op == OP_ENDTRY ? $stack-- |
520
|
3
|
|
|
|
|
8
|
: $op == OP_WHILE ? push @{ $self->{iter} }, [ 1, $self->{pc} ] |
521
|
75
|
100
|
100
|
|
|
412
|
: $op == OP_ENDWHILE ? pop @{ $self->{iter} } |
|
|
50
|
66
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
522
|
|
|
|
|
|
|
: next; |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
|
525
|
58
|
100
|
|
|
|
116
|
if ($self->{pc} > $#{ $self->{opcode} }) { |
|
58
|
|
|
|
|
226
|
|
526
|
6
|
100
|
|
|
|
19
|
if ($self->{parent}) { |
527
|
4
|
|
|
|
|
25
|
return $self->{parent}->throw($err); |
528
|
|
|
|
|
|
|
} else { |
529
|
2
|
|
|
|
|
41
|
croak 'uncatched exception in Defer: '.$err; |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
52
|
|
|
|
|
153
|
my ($op, @param) = _op($self); |
534
|
52
|
100
|
|
|
|
154
|
if ($op == OP_CATCH) { |
535
|
44
|
|
|
|
|
145
|
while (my ($cond, $code) = splice @param, 0, 2) { |
536
|
49
|
100
|
|
|
|
299
|
if ($err =~ /$cond/ms) { |
537
|
39
|
|
|
|
|
110
|
return $code->($this, $err); |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
# Re-throw exception if no one regex in this OP_CATCH match it. |
541
|
5
|
|
|
|
|
27
|
return $this->throw($err); |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
else { # OP_FINALLY |
544
|
|
|
|
|
|
|
# If OP_FINALLY ends with done() - call throw($err) again instead. |
545
|
8
|
|
|
|
|
47
|
$self->{findone} = ['throw', $err]; |
546
|
8
|
|
|
|
|
21
|
return $param[0]->($this, $err); |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
1; # Magic true value required at end of module |
552
|
|
|
|
|
|
|
__END__ |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=encoding utf8 |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
=head1 NAME |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
Async::Defer - VM to write and run async code in usual sync-like way |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=head1 SYNOPSIS |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
use Async::Defer; |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
# ... CREATE |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
my $defer = Async::Defer->new(); |
568
|
|
|
|
|
|
|
my $defer2 = $defer->clone(); |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# ... SETUP |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
$defer->do(sub{ |
573
|
|
|
|
|
|
|
my ($d, @param) = @_; |
574
|
|
|
|
|
|
|
# run sync/async code which MUST end with one of: |
575
|
|
|
|
|
|
|
# $d->done(@result); |
576
|
|
|
|
|
|
|
# $d->throw($error); |
577
|
|
|
|
|
|
|
# $d->continue(); |
578
|
|
|
|
|
|
|
# $d->break(); |
579
|
|
|
|
|
|
|
}); |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
$defer->if(sub{ my $d=shift; return 1 }); |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
$defer->try(); |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
$defer->do($defer2); |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
$defer->catch( |
588
|
|
|
|
|
|
|
qr/^io:/ => sub{ |
589
|
|
|
|
|
|
|
my ($d,$err) = @_; |
590
|
|
|
|
|
|
|
# end with $d->done/throw/continue/break |
591
|
|
|
|
|
|
|
}, |
592
|
|
|
|
|
|
|
qr/.*/ => sub{ # WILL CATCH ALL EXCEPTIONS |
593
|
|
|
|
|
|
|
my ($d,$err) = @_; |
594
|
|
|
|
|
|
|
# end with $d->done/throw/continue/break |
595
|
|
|
|
|
|
|
}, |
596
|
|
|
|
|
|
|
FINALLY => sub{ |
597
|
|
|
|
|
|
|
my ($d,$err,@result) = @_; |
598
|
|
|
|
|
|
|
# end with $d->done/throw/continue/break |
599
|
|
|
|
|
|
|
}, |
600
|
|
|
|
|
|
|
); |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
$defer->else(); |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
$defer->while(sub{ my $d=shift; return $d->iter() <= 3 }); |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
$defer->do(sub{ |
607
|
|
|
|
|
|
|
my ($d) = @_; |
608
|
|
|
|
|
|
|
# may access $d->iter() here |
609
|
|
|
|
|
|
|
# end with $d->done/throw/continue/break |
610
|
|
|
|
|
|
|
}); |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
$defer->end_while(); |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
$defer->end_if(); |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
$defer->{anyvar} = 'anyval'; |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
# ... START |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
$defer->run(); |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=head1 DESCRIPTION |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
B<WARNING: This is experimental code, public interface may change.> |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
This module's goal is to simplify writing complex async event-based code, |
628
|
|
|
|
|
|
|
which usually mean huge amount of callback/errback functions, very hard to |
629
|
|
|
|
|
|
|
support. It was initially inspired by Python/Twisted's |
630
|
|
|
|
|
|
|
L<Deferred|http://twistedmatrix.com/documents/10.1.0/core/howto/defer.html> |
631
|
|
|
|
|
|
|
object, but go further and provide virtual machine which allow you to |
632
|
|
|
|
|
|
|
write/define complete async program (which consists of many |
633
|
|
|
|
|
|
|
callback/errback) in sync way, just like you write usual non-async |
634
|
|
|
|
|
|
|
programs. |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
Main idea is simple. For example, if you've this non-async code: |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
$var = fetch_val(); |
639
|
|
|
|
|
|
|
process_val( $var ); |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
and want to make C<fetch_val()> async, you usually do something like this: |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
fetch_val( cb => \&value_fetched ); |
644
|
|
|
|
|
|
|
sub value_fetched { |
645
|
|
|
|
|
|
|
my ($var) = @_; |
646
|
|
|
|
|
|
|
process_val( $var ); |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
With Async::Defer you will split initial non-async code in sync parts (usually |
650
|
|
|
|
|
|
|
this mean - split on assignment operator): |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
### 1 |
653
|
|
|
|
|
|
|
fetch_val(); |
654
|
|
|
|
|
|
|
### 2 |
655
|
|
|
|
|
|
|
$var = |
656
|
|
|
|
|
|
|
process_val( $var ); |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
then wrap each part in separate anon sub and add Defer object to join |
659
|
|
|
|
|
|
|
these parts together: |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
$d = Async::Defer->new(); |
662
|
|
|
|
|
|
|
$d->do(sub{ |
663
|
|
|
|
|
|
|
my ($d) = @_; |
664
|
|
|
|
|
|
|
fetch_val( $d ); # will call $d->done('â¦resultâ¦') when done |
665
|
|
|
|
|
|
|
}); |
666
|
|
|
|
|
|
|
$d->do(sub{ |
667
|
|
|
|
|
|
|
my ($d, $var) = @_; |
668
|
|
|
|
|
|
|
process_val( $var ); |
669
|
|
|
|
|
|
|
$d->done(); # this sub is sync, it call done() immediately |
670
|
|
|
|
|
|
|
}); |
671
|
|
|
|
|
|
|
$d->run(); |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
These anon subs are similar to I<statements> in perl. Between these |
674
|
|
|
|
|
|
|
I<statements> you can use I<flow control> operators like C<if()>, |
675
|
|
|
|
|
|
|
C<while()> and C<try()>/C<catch()>. And inside I<statements> you can |
676
|
|
|
|
|
|
|
control execution flow using C<done()>, C<throw()>, C<continue()> |
677
|
|
|
|
|
|
|
and C<break()> operators when current async function will finish and |
678
|
|
|
|
|
|
|
will be ready to go to the continue step. |
679
|
|
|
|
|
|
|
Finally, you can use Async::Defer object to keep your I<local variables> - |
680
|
|
|
|
|
|
|
this object is empty hash, and you can create any keys in it. |
681
|
|
|
|
|
|
|
Single Defer object described this way is sort of single I<function>. |
682
|
|
|
|
|
|
|
And it's possible to I<call> another functions by using another Defer |
683
|
|
|
|
|
|
|
object as parameter for C<do()> instead of usual anon sub. |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
While you can use both sync and async sub in C<do()>, they all B<MUST> |
686
|
|
|
|
|
|
|
call one of C<done()>, C<throw()>, C<continue()> or C<break()> when they finish |
687
|
|
|
|
|
|
|
their work, and do this B<ONLY ONCE>. This is Defer's way to proceed from |
688
|
|
|
|
|
|
|
one step to another, and if not done right Defer object's behaviour is |
689
|
|
|
|
|
|
|
undefined! |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=head2 PERSISTENT STATE, LOCAL VARIABLES and SCOPE |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
There are several ways to implement this, and it's unclear yet which |
695
|
|
|
|
|
|
|
way is the best. We can implement full-featured stack with local variables |
696
|
|
|
|
|
|
|
similar to perl's C<local> using getter/setter methods; we can fill called |
697
|
|
|
|
|
|
|
Defer objects with copy of all keys in parent Defer object (so called |
698
|
|
|
|
|
|
|
object will have full read-only access to parent's scalar data, and read/write |
699
|
|
|
|
|
|
|
access to parent's reference data types); we can do nothing and let user |
700
|
|
|
|
|
|
|
manually send all needed data to called Defer object as params and get |
701
|
|
|
|
|
|
|
data back using returned values (by C<done()> or C<throw()>). |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
In current implementation we do nothing, so here is some ways to go: |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
### @results = another_defer(@params) |
706
|
|
|
|
|
|
|
$d->do(sub{ |
707
|
|
|
|
|
|
|
my ($d) = @_; |
708
|
|
|
|
|
|
|
my @params_for_another_defer = (â¦); |
709
|
|
|
|
|
|
|
$d->done(@params_for_another_defer); |
710
|
|
|
|
|
|
|
}); |
711
|
|
|
|
|
|
|
$d->do($another_defer); |
712
|
|
|
|
|
|
|
$d->do(sub{ |
713
|
|
|
|
|
|
|
my ($d, @results_from_another_defer) = @_; |
714
|
|
|
|
|
|
|
... |
715
|
|
|
|
|
|
|
$d->done(); |
716
|
|
|
|
|
|
|
}); |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
### share some local variables with $another_defer |
719
|
|
|
|
|
|
|
$d->do(sub{ |
720
|
|
|
|
|
|
|
my ($d) = @_; |
721
|
|
|
|
|
|
|
$d->{readonly} = $scalar; |
722
|
|
|
|
|
|
|
$d->{readwrite} = $ref_to_something; |
723
|
|
|
|
|
|
|
$another_defer->{readonly} = $d->{readonly}; |
724
|
|
|
|
|
|
|
$another_defer->{readwrite} = $d->{readwrite}; |
725
|
|
|
|
|
|
|
$d->done(); |
726
|
|
|
|
|
|
|
}); |
727
|
|
|
|
|
|
|
$d->do($another_defer); |
728
|
|
|
|
|
|
|
$d->do(sub{ |
729
|
|
|
|
|
|
|
my ($d) = @_; |
730
|
|
|
|
|
|
|
# $d->{readwrite} here may be modifed by $another_defer |
731
|
|
|
|
|
|
|
$d->done(); |
732
|
|
|
|
|
|
|
}); |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
### share all variables with $another_defer (run it manually) |
735
|
|
|
|
|
|
|
$d->do(sub{ |
736
|
|
|
|
|
|
|
my ($d) = @_; |
737
|
|
|
|
|
|
|
%$another_defer = %$d; |
738
|
|
|
|
|
|
|
$another_defer->run($d); |
739
|
|
|
|
|
|
|
}); |
740
|
|
|
|
|
|
|
$d->do(sub{ |
741
|
|
|
|
|
|
|
my ($d) = @_; |
742
|
|
|
|
|
|
|
# all reference-type keys in $d may be modifed by $another_defer |
743
|
|
|
|
|
|
|
$d->done(); |
744
|
|
|
|
|
|
|
}); |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
If you want to reuse same Defer object several times, then you should keep |
747
|
|
|
|
|
|
|
in mind: keys created inside this object on first run won't be automatically |
748
|
|
|
|
|
|
|
removed, so on second and continue runs it will see internal data left by |
749
|
|
|
|
|
|
|
previous runs. This may or may not be desirable behaviour. In later case |
750
|
|
|
|
|
|
|
you should use C<clone()> and run only clones of original object (clones are |
751
|
|
|
|
|
|
|
created using C<%$clone=%$orig>, so they share only reference-type keys |
752
|
|
|
|
|
|
|
which exists in original Defer): |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
$d->do( $another_defer->clone() ); |
755
|
|
|
|
|
|
|
$d->do( $another_defer->clone() ); |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=head2 NESTED DEFERS |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
Async::Defer objects can be nested, and there are two ways to do it. |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
One way is to add a child defer to the parent defer using C<do()> method. |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
my $cd = Async::Defer->new(); |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
## setup the child defer. |
766
|
|
|
|
|
|
|
$cd->do( ... ); |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
## parent defer |
769
|
|
|
|
|
|
|
my $pd = Async::Defer->new(); |
770
|
|
|
|
|
|
|
$pd->do( ... ); |
771
|
|
|
|
|
|
|
$pd->do(sub { |
772
|
|
|
|
|
|
|
my $d = shift; |
773
|
|
|
|
|
|
|
...; |
774
|
|
|
|
|
|
|
$d->done( @arguments_for_child_defer ); |
775
|
|
|
|
|
|
|
}); |
776
|
|
|
|
|
|
|
## run the child defer |
777
|
|
|
|
|
|
|
$pd->do($cd); |
778
|
|
|
|
|
|
|
$pd->do(sub { |
779
|
|
|
|
|
|
|
my ($d, @results_from_child_defer) = @_; |
780
|
|
|
|
|
|
|
...; |
781
|
|
|
|
|
|
|
}); |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
The other way is to call C<run()> on the child defer with its first |
784
|
|
|
|
|
|
|
argument being the parent defer. This is very useful when you dynamically |
785
|
|
|
|
|
|
|
create the child defer in statements of the parent defer. |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
## parent defer |
788
|
|
|
|
|
|
|
my $pd = Async::Defer->new(); |
789
|
|
|
|
|
|
|
$pd->do(sub { |
790
|
|
|
|
|
|
|
my ($d, @args) = @_; |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
## create the child defer in the statement |
793
|
|
|
|
|
|
|
my $cd = Async::Defer->new(); |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
## setup the child defer |
796
|
|
|
|
|
|
|
$cd->do( ... ); |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
## run() the child. |
799
|
|
|
|
|
|
|
## You do not have to call $d->done explicitly, |
800
|
|
|
|
|
|
|
## because the flow continues from the child to the parent. |
801
|
|
|
|
|
|
|
$cd->run($d, @argments_for_child_defer); |
802
|
|
|
|
|
|
|
}); |
803
|
|
|
|
|
|
|
$pd->do(sub { |
804
|
|
|
|
|
|
|
my ($d, @results_from_child_defer) = @_; |
805
|
|
|
|
|
|
|
...; |
806
|
|
|
|
|
|
|
}); |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
=head1 EXPORTS |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
Nothing. |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
=head1 INTERFACE |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
=over |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
=item new() |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
Create and return Async::Defer object. |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
=item clone() |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
Clone existing Async::Defer object and return clone. |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
Clone will have same I<program> (I<STATEMENTS> and I<OPERATORS> added to |
827
|
|
|
|
|
|
|
original object) and same I<local variables> (non-deep copy of orig object |
828
|
|
|
|
|
|
|
keys using C<%$clone=%$orig>). After cloning these two objects can be |
829
|
|
|
|
|
|
|
modified (by adding new I<STATEMENTS>, I<OPERATORS> and modifying variables) |
830
|
|
|
|
|
|
|
independently. |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
It's possible to C<clone()> object which is running right now, cloned object |
833
|
|
|
|
|
|
|
will not be in running state - this is safe way to C<run()> objects which may |
834
|
|
|
|
|
|
|
or may not be already running. |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
=item run( [ $parent_defer, @params ] ) |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
=item run( [ \&callback, @params ] ) |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
Start executing object's current I<program>, which must be defined first by |
841
|
|
|
|
|
|
|
adding at least one I<STATEMENT> (C<do()> or C<<catch(FINALLY=>sub{})>>) |
842
|
|
|
|
|
|
|
to this object. |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
Usually while C<run()> only first I<STATEMENT> will be executed (with optional |
845
|
|
|
|
|
|
|
C<@params> in parameters). It will just start some async function and |
846
|
|
|
|
|
|
|
returns, and C<run()> will returns immediately after this too. Actual |
847
|
|
|
|
|
|
|
execution of this object will continue when started async function will |
848
|
|
|
|
|
|
|
finish (usually after Timer or I/O event) and call this object's C<done()>, |
849
|
|
|
|
|
|
|
C<break()>, C<continue()> or C<throw()> methods. |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
It's possible to make all I<STATEMENTS> sync - in this case full I<program> |
852
|
|
|
|
|
|
|
will be executed before returning from C<run()> - but this has no real sense |
853
|
|
|
|
|
|
|
because you don't need Defer object for sync programs. |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
If C<run()> used to start top-level I<program> (i.e. without C<$parent_defer> |
856
|
|
|
|
|
|
|
parameter), then there will be no I<return value> at end of I<program> - |
857
|
|
|
|
|
|
|
after break I<STATEMENT> in this object will call C<done()> nothing else will |
858
|
|
|
|
|
|
|
happens and any parameters of that break C<done()> call will be ignored. |
859
|
|
|
|
|
|
|
If this Defer object was started as part of another I<program> (i.e. it was |
860
|
|
|
|
|
|
|
added there using C<do()> or just manually executed from some I<STATEMENT> with |
861
|
|
|
|
|
|
|
defined C<$parent_defer> parameter), then it I<return value> will be delivered |
862
|
|
|
|
|
|
|
to continue I<STATEMENT> in C<$parent_defer> object (See L</"NESTED DEFERS">). |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
The first argument for C<run()> may also be a subroutine reference (C<\&callback>). |
865
|
|
|
|
|
|
|
In this case, the callback is called after break I<STATEMENT> in this object. |
866
|
|
|
|
|
|
|
The arguments for the callback are the results of the break I<STATEMENT>. |
867
|
|
|
|
|
|
|
Any value returned from C<\&callback> will be ignored. |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
=item iter() |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
This method available only inside C<while()> - both in C<while()>'s |
872
|
|
|
|
|
|
|
C<\&conditional> argument and C<while()>'s body I<STATEMENTS>. It return |
873
|
|
|
|
|
|
|
current iteration number for nearest C<while()>, starting from 1. |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
# this loop will execute 3 times: |
876
|
|
|
|
|
|
|
$d->while(sub{ shift->iter() <= 3 }); |
877
|
|
|
|
|
|
|
$d->do(sub{ |
878
|
|
|
|
|
|
|
my ($d) = @_; |
879
|
|
|
|
|
|
|
printf "Iteration %d\n", $d->iter(); |
880
|
|
|
|
|
|
|
$d->done(); |
881
|
|
|
|
|
|
|
}); |
882
|
|
|
|
|
|
|
$d->end_while(); |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
=back |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
=head2 STATEMENTS and OPERATORS |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
All I<STATEMENTS> methods return the Async::Defer object, |
889
|
|
|
|
|
|
|
so that you can chain method calls. |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
=over |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
=item do( \&sync_or_async_code, ⦠) |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
=item do( $child_defer, ⦠) |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
Add I<STATEMENT> to this object's I<program>. |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
When this I<STATEMENT> should be executed, C<\&sync_or_async_code> |
900
|
|
|
|
|
|
|
(or C<$child_defer>'s first I<STATEMENT>) will be called with these params: |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
( $defer_object, @optional_results_from_previous_STATEMENT ) |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
C<do()> accepts multiple arguments. Those I<STATEMENT>s are added to the object |
905
|
|
|
|
|
|
|
in that order, and can be mix of any types - i.e. it's same as call C<do()> |
906
|
|
|
|
|
|
|
sequentially multiple times providing these arguments one-by-one. |
907
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
do( |
909
|
|
|
|
|
|
|
\&code, |
910
|
|
|
|
|
|
|
$defer, |
911
|
|
|
|
|
|
|
[$defer1, $defer2, \&code3], |
912
|
|
|
|
|
|
|
{ |
913
|
|
|
|
|
|
|
task1 => $defer4, |
914
|
|
|
|
|
|
|
task2 => \&code5, |
915
|
|
|
|
|
|
|
}, |
916
|
|
|
|
|
|
|
\&more_code, |
917
|
|
|
|
|
|
|
⦠|
918
|
|
|
|
|
|
|
); |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
=item do( [\&sync_or_async_code, $child_defer, â¦], ⦠) |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
=item do( {task1=>\&sync_or_async_code, task2=>$child_defer, â¦}, ⦠) |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
Add one I<STATEMENT> to this object's I<program>. |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
When this I<STATEMENT> should be executed, all these tasks will be started |
927
|
|
|
|
|
|
|
simultaneously (Defer objects using C<clone()> and C<run()>, code by |
928
|
|
|
|
|
|
|
transforming into new Defer object and then also C<run()>). |
929
|
|
|
|
|
|
|
This I<program> will continue only after all these tasks will be finished |
930
|
|
|
|
|
|
|
(either with C<done()> or C<throw()>). |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
It's possible to provide params individually for each of these tasks and |
933
|
|
|
|
|
|
|
receive results/error returned by each of these tasks, but actual syntax |
934
|
|
|
|
|
|
|
depends on how these tasks was named - by id (ARRAY) or by name (HASH): |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
$d->do(sub{ |
937
|
|
|
|
|
|
|
my ($d) = @_; |
938
|
|
|
|
|
|
|
$d->done( |
939
|
|
|
|
|
|
|
['param1 for task1', 'param2 for task1'], |
940
|
|
|
|
|
|
|
['param1 for task2'], |
941
|
|
|
|
|
|
|
[undef, 'param2 for task3'], |
942
|
|
|
|
|
|
|
# no params for task4,task5,⦠|
943
|
|
|
|
|
|
|
); |
944
|
|
|
|
|
|
|
}); |
945
|
|
|
|
|
|
|
$d->do([ $d_task1, $d_task2, $d_task3, $d_some, $d_some ]); |
946
|
|
|
|
|
|
|
$d->do(sub{ |
947
|
|
|
|
|
|
|
my ($d, @taskresults) = @_; |
948
|
|
|
|
|
|
|
my $id = 1; |
949
|
|
|
|
|
|
|
if (ref $taskresults[$id-1]) { |
950
|
|
|
|
|
|
|
print "task $id results:", @{ $taskresults[$id-1] }; |
951
|
|
|
|
|
|
|
} else { |
952
|
|
|
|
|
|
|
print "task $id throw error:", $taskresults[$id-1]; |
953
|
|
|
|
|
|
|
} |
954
|
|
|
|
|
|
|
}); |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
$d->do(sub{ |
957
|
|
|
|
|
|
|
my ($d) = @_; |
958
|
|
|
|
|
|
|
$d->done( |
959
|
|
|
|
|
|
|
task1 => ['param1 for task1', 'param2 for task1'], |
960
|
|
|
|
|
|
|
task2 => ['param1 for task2'], |
961
|
|
|
|
|
|
|
task3 => [undef, 'param2 for task3'], |
962
|
|
|
|
|
|
|
# no params for task4,task5,⦠|
963
|
|
|
|
|
|
|
); |
964
|
|
|
|
|
|
|
}); |
965
|
|
|
|
|
|
|
$d->do({ |
966
|
|
|
|
|
|
|
task1 => $d_task1, |
967
|
|
|
|
|
|
|
task2 => $d_task2, |
968
|
|
|
|
|
|
|
task3 => $d_task3, |
969
|
|
|
|
|
|
|
task4 => $d_some, |
970
|
|
|
|
|
|
|
task5 => $d_some, |
971
|
|
|
|
|
|
|
}); |
972
|
|
|
|
|
|
|
$d->do(sub{ |
973
|
|
|
|
|
|
|
my ($d, %taskresults) = @_; |
974
|
|
|
|
|
|
|
if (ref $taskresults{task1}) { |
975
|
|
|
|
|
|
|
print "task1 results:", @{ $taskresults{task1} }; |
976
|
|
|
|
|
|
|
} else { |
977
|
|
|
|
|
|
|
print "task1 throw error:", $taskresults{task1}; |
978
|
|
|
|
|
|
|
} |
979
|
|
|
|
|
|
|
}); |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
=item if( \&conditional ) |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
=item else() |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
=item end_if() |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
Add conditional I<OPERATOR> to this object's I<program>. |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
When this I<OPERATOR> should be executed, C<\&conditional> will be called |
990
|
|
|
|
|
|
|
with single param: |
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
( $defer_object ) |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
The C<\&conditional> B<MUST> be sync, and return true/false. |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
=item while( \&conditional ) |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
=item end_while() |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
Add loop I<OPERATOR> to this object's I<program>. |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
When this I<OPERATOR> should be executed, C<\&conditional> will be called with |
1003
|
|
|
|
|
|
|
single param: |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
( $defer_object ) |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
The C<\&conditional> B<MUST> be sync, and return true/false. |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
=item try() |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
=item catch( $regex_or_FINALLY => \&sync_or_async_code, ... ) |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
Add exception handling to this object's I<program>. |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
In general, try/catch/finally behaviour is same as in Java (and probably |
1016
|
|
|
|
|
|
|
many other languages). |
1017
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
If some I<STATEMENTS> inside try/catch block will C<throw()>, the thrown error |
1019
|
|
|
|
|
|
|
can be intercepted (using matching regexp in C<catch()>) and handled in any |
1020
|
|
|
|
|
|
|
way (blocked - if C<catch()> handler call C<done()>, C<continue()> or C<break()> or |
1021
|
|
|
|
|
|
|
replaced by another exception - if C<catch()> handler call C<throw()>). |
1022
|
|
|
|
|
|
|
If exception match more than one regexp, first successfully matched |
1023
|
|
|
|
|
|
|
regexp's handler will be used. Handler will be executed with params: |
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
( $defer_object, $error ) |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
In addition to exception handlers you can also define FINALLY handler |
1028
|
|
|
|
|
|
|
(by using string C<"FINALLY"> instead of regex). FINALLY handler will be |
1029
|
|
|
|
|
|
|
called in any case (with/without exception) and may handle this in any way |
1030
|
|
|
|
|
|
|
just like any other exception handler in C<catch()>. FINALLY handler will |
1031
|
|
|
|
|
|
|
be executed with different params: |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
# with exception |
1034
|
|
|
|
|
|
|
( $defer_object, $error) |
1035
|
|
|
|
|
|
|
# without exception |
1036
|
|
|
|
|
|
|
( $defer_object, @optional_results_from_previous_STATEMENT ) |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
=back |
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
=head2 FLOW CONTROL in STATEMENTS |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
Unless you are nesting child defers, one and only one of these methods B<MUST> be |
1043
|
|
|
|
|
|
|
called at end of each I<STATEMENT>, both sync and async! |
1044
|
|
|
|
|
|
|
In the case of nested defers, see L</"NESTED DEFERS">. |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
=over |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
=item done( @optional_result ) |
1049
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
Go to continue I<STATEMENT>/I<OPERATOR>. If continue is I<STATEMENT>, it will receive |
1051
|
|
|
|
|
|
|
C<@optional_result> in it parameters. |
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
=item throw( $error ) |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
Throw exception. Nearest matching C<catch()> or FINALLY I<STATEMENT> will be |
1056
|
|
|
|
|
|
|
executed and receive C<$error> in it parameter. |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
=item continue() |
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
Move to beginning of nearest C<while()> (or to first I<STATEMENT> if |
1061
|
|
|
|
|
|
|
called outside C<while()>) and continue with continue iteration (if C<while()>'s |
1062
|
|
|
|
|
|
|
C<\&conditional> still returns true). |
1063
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
=item break() |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
Move to first I<STATEMENT>/I<OPERATOR> after nearest C<while()> (or finish this |
1067
|
|
|
|
|
|
|
I<program> if called outside C<while()> - returning to parent's Defer object |
1068
|
|
|
|
|
|
|
if any). |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
=back |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
No bugs have been reported. |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
=head1 SUPPORT |
1079
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
Please report any bugs or feature requests through the web interface at |
1081
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Async-Defer>. |
1082
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress |
1083
|
|
|
|
|
|
|
on your bug as I make changes. |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
You can also look for information at: |
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
=over |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Async-Defer> |
1092
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
1094
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
L<http://annocpan.org/dist/Async-Defer> |
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
=item * CPAN Ratings |
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
L<http://cpanratings.perl.org/d/Async-Defer> |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
=item * Search CPAN |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
L<http://search.cpan.org/dist/Async-Defer/> |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
=back |
1106
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
=head1 AUTHOR |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
Alex Efros C<< <powerman-asdf@ya.ru> >> |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
=head1 CONTRIBUTORS |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
Toshio Ito C<< toshioito [at] cpan.org >> |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
Copyright 2011,2012 Alex Efros <powerman-asdf@ya.ru>. |
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
This program is distributed under the MIT (X11) License: |
1123
|
|
|
|
|
|
|
L<http://www.opensource.org/licenses/mit-license.php> |
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person |
1126
|
|
|
|
|
|
|
obtaining a copy of this software and associated documentation |
1127
|
|
|
|
|
|
|
files (the "Software"), to deal in the Software without |
1128
|
|
|
|
|
|
|
restriction, including without limitation the rights to use, |
1129
|
|
|
|
|
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell |
1130
|
|
|
|
|
|
|
copies of the Software, and to permit persons to whom the |
1131
|
|
|
|
|
|
|
Software is furnished to do so, subject to the following |
1132
|
|
|
|
|
|
|
conditions: |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be |
1135
|
|
|
|
|
|
|
included in all copies or substantial portions of the Software. |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
1138
|
|
|
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
1139
|
|
|
|
|
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
1140
|
|
|
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
1141
|
|
|
|
|
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
1142
|
|
|
|
|
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
1143
|
|
|
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
1144
|
|
|
|
|
|
|
OTHER DEALINGS IN THE SOFTWARE. |
1145
|
|
|
|
|
|
|
|