line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Object::Remote::Connection; |
2
|
|
|
|
|
|
|
|
3
|
11
|
|
|
11
|
|
63947
|
use Object::Remote::Logging qw (:log :dlog router); |
|
11
|
|
|
|
|
42
|
|
|
11
|
|
|
|
|
95
|
|
4
|
11
|
|
|
11
|
|
444
|
use Object::Remote::Future; |
|
11
|
|
|
|
|
23
|
|
|
11
|
|
|
|
|
635
|
|
5
|
11
|
|
|
11
|
|
4170
|
use Object::Remote::Null; |
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
318
|
|
6
|
11
|
|
|
11
|
|
394
|
use Object::Remote::Handle; |
|
11
|
|
|
|
|
21
|
|
|
11
|
|
|
|
|
241
|
|
7
|
11
|
|
|
11
|
|
3716
|
use Object::Remote::CodeContainer; |
|
11
|
|
|
|
|
25
|
|
|
11
|
|
|
|
|
274
|
|
8
|
11
|
|
|
11
|
|
3694
|
use Object::Remote::GlobProxy; |
|
11
|
|
|
|
|
28
|
|
|
11
|
|
|
|
|
284
|
|
9
|
11
|
|
|
11
|
|
3776
|
use Object::Remote::GlobContainer; |
|
11
|
|
|
|
|
29
|
|
|
11
|
|
|
|
|
334
|
|
10
|
11
|
|
|
11
|
|
4521
|
use Object::Remote::Tied; |
|
11
|
|
|
|
|
28
|
|
|
11
|
|
|
|
|
293
|
|
11
|
11
|
|
|
11
|
|
402
|
use Object::Remote; |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
201
|
|
12
|
11
|
|
|
11
|
|
47
|
use Symbol; |
|
11
|
|
|
|
|
19
|
|
|
11
|
|
|
|
|
503
|
|
13
|
11
|
|
|
11
|
|
59
|
use IO::Handle; |
|
11
|
|
|
|
|
19
|
|
|
11
|
|
|
|
|
290
|
|
14
|
11
|
|
|
11
|
|
4555
|
use POSIX ":sys_wait_h"; |
|
11
|
|
|
|
|
56623
|
|
|
11
|
|
|
|
|
53
|
|
15
|
11
|
|
|
11
|
|
13792
|
use Module::Runtime qw(use_module); |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
80
|
|
16
|
11
|
|
|
11
|
|
511
|
use Scalar::Util qw(weaken blessed refaddr openhandle); |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
626
|
|
17
|
11
|
|
|
11
|
|
6831
|
use JSON::PP qw(encode_json); |
|
11
|
|
|
|
|
129228
|
|
|
11
|
|
|
|
|
654
|
|
18
|
11
|
|
|
11
|
|
76
|
use Future; |
|
11
|
|
|
|
|
19
|
|
|
11
|
|
|
|
|
1093
|
|
19
|
11
|
|
|
11
|
|
49
|
use Carp qw(croak); |
|
11
|
|
|
|
|
21
|
|
|
11
|
|
|
|
|
414
|
|
20
|
11
|
|
|
11
|
|
55
|
use Moo; |
|
11
|
|
|
|
|
19
|
|
|
11
|
|
|
|
|
84
|
|
21
|
|
|
|
|
|
|
|
22
|
11
|
|
|
11
|
|
5864
|
BEGIN { router()->exclude_forwarding } |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
END { |
25
|
11
|
|
|
11
|
|
11587
|
our %child_pids; |
26
|
|
|
|
|
|
|
|
27
|
11
|
|
|
|
|
205
|
log_trace { "END handler is being invoked in " . __PACKAGE__ }; |
|
0
|
|
|
|
|
0
|
|
28
|
|
|
|
|
|
|
|
29
|
11
|
|
|
|
|
146
|
foreach(keys(%child_pids)) { |
30
|
18
|
|
|
|
|
398
|
log_debug { "Killing child process '$_'" }; |
|
0
|
|
|
|
|
0
|
|
31
|
18
|
|
|
|
|
1285
|
kill('TERM', $_); |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
has _id => ( is => 'ro', required => 1, default => sub { our $NEXT_CONNECTION_ID++ } ); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
has send_to_fh => ( |
38
|
|
|
|
|
|
|
is => 'ro', required => 1, |
39
|
|
|
|
|
|
|
trigger => sub { |
40
|
|
|
|
|
|
|
my $self = $_[0]; |
41
|
|
|
|
|
|
|
$_[1]->autoflush(1); |
42
|
|
|
|
|
|
|
Dlog_trace { my $id = $self->_id; "connection had send_to_fh set to $_" } $_[1]; |
43
|
|
|
|
|
|
|
}, |
44
|
|
|
|
|
|
|
); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
has read_channel => ( |
47
|
|
|
|
|
|
|
is => 'ro', required => 1, |
48
|
|
|
|
|
|
|
trigger => sub { |
49
|
|
|
|
|
|
|
my ($self, $ch) = @_; |
50
|
|
|
|
|
|
|
my $id = $self->_id; |
51
|
|
|
|
|
|
|
Dlog_trace { "trigger for read_channel has been invoked for connection $id; file handle is $_" } $ch->fh; |
52
|
|
|
|
|
|
|
weaken($self); |
53
|
|
|
|
|
|
|
$ch->on_line_call(sub { $self->_receive(@_) }); |
54
|
|
|
|
|
|
|
$ch->on_close_call(sub { |
55
|
|
|
|
|
|
|
log_trace { "invoking 'done' on on_close handler for connection id '$id'" }; |
56
|
|
|
|
|
|
|
$self->on_close->done(@_); |
57
|
|
|
|
|
|
|
}); |
58
|
|
|
|
|
|
|
}, |
59
|
|
|
|
|
|
|
); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
has on_close => ( |
62
|
|
|
|
|
|
|
is => 'rw', default => sub { $_[0]->_install_future_handlers(Future->new) }, |
63
|
|
|
|
|
|
|
trigger => sub { |
64
|
|
|
|
|
|
|
log_trace { "Installing handlers into future via trigger" }; |
65
|
|
|
|
|
|
|
$_[0]->_install_future_handlers($_[1]) |
66
|
|
|
|
|
|
|
}, |
67
|
|
|
|
|
|
|
); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
has child_pid => (is => 'ro'); |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
has local_objects_by_id => ( |
72
|
|
|
|
|
|
|
is => 'ro', default => sub { {} }, |
73
|
|
|
|
|
|
|
coerce => sub { +{ %{$_[0]} } }, # shallow clone on the way in |
74
|
|
|
|
|
|
|
); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
has remote_objects_by_id => ( |
77
|
|
|
|
|
|
|
is => 'ro', default => sub { {} }, |
78
|
|
|
|
|
|
|
coerce => sub { +{ %{$_[0]} } }, # shallow clone on the way in |
79
|
|
|
|
|
|
|
); |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
has outstanding_futures => (is => 'ro', default => sub { {} }); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
has _json => ( |
84
|
|
|
|
|
|
|
is => 'lazy', |
85
|
|
|
|
|
|
|
handles => { |
86
|
|
|
|
|
|
|
_deserialize => 'decode', |
87
|
|
|
|
|
|
|
_encode => 'encode', |
88
|
|
|
|
|
|
|
}, |
89
|
|
|
|
|
|
|
); |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
after BUILD => sub { |
92
|
|
|
|
|
|
|
my ($self) = @_; |
93
|
|
|
|
|
|
|
my $pid = $self->child_pid; |
94
|
|
|
|
|
|
|
our %child_pids; |
95
|
|
|
|
|
|
|
return unless defined $pid; |
96
|
|
|
|
|
|
|
$child_pids{$pid} = 1; |
97
|
|
|
|
|
|
|
return; |
98
|
|
|
|
|
|
|
}; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub BUILD { } |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub is_valid { |
103
|
310
|
|
|
310
|
0
|
3284
|
my ($self) = @_; |
104
|
310
|
|
|
|
|
4996
|
my $valid = ! $self->on_close->is_ready; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
log_trace { |
107
|
0
|
|
|
0
|
|
0
|
my $id = $self->_id; |
108
|
0
|
|
|
|
|
0
|
my $text; |
109
|
0
|
0
|
|
|
|
0
|
if ($valid) { |
110
|
0
|
|
|
|
|
0
|
$text = 'yes'; |
111
|
|
|
|
|
|
|
} else { |
112
|
0
|
|
|
|
|
0
|
$text = 'no'; |
113
|
|
|
|
|
|
|
} |
114
|
0
|
|
|
|
|
0
|
"Connection '$id' is valid: '$text'" |
115
|
310
|
|
|
|
|
4237
|
}; |
116
|
|
|
|
|
|
|
|
117
|
310
|
|
|
|
|
3662
|
return $valid; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub _fail_outstanding { |
121
|
1
|
|
|
1
|
|
14
|
my ($self, $error) = @_; |
122
|
1
|
|
|
|
|
6
|
my $outstanding = $self->outstanding_futures; |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Dlog_debug { |
125
|
0
|
|
|
0
|
|
0
|
sprintf "Failing %i outstanding futures with '$error'", scalar(keys(%$outstanding)) |
126
|
1
|
|
|
|
|
6
|
}; |
127
|
|
|
|
|
|
|
|
128
|
1
|
|
|
|
|
13
|
foreach(keys(%$outstanding)) { |
129
|
3
|
|
|
0
|
|
45
|
log_trace { "Failing future for $_" }; |
|
0
|
|
|
|
|
0
|
|
130
|
3
|
|
|
|
|
26
|
my $future = $outstanding->{$_}; |
131
|
3
|
|
|
|
|
13
|
$future->fail("$error\n"); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
1
|
|
|
|
|
7
|
%$outstanding = (); |
135
|
1
|
|
|
|
|
3
|
return; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub _install_future_handlers { |
139
|
19
|
|
|
19
|
|
415
|
my ($self, $f) = @_; |
140
|
19
|
|
|
|
|
65
|
our %child_pids; |
141
|
19
|
|
|
0
|
|
458
|
Dlog_trace { "Installing handlers into future for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
142
|
19
|
|
|
|
|
303
|
weaken($self); |
143
|
|
|
|
|
|
|
$f->on_done(sub { |
144
|
1
|
|
|
1
|
|
91
|
my $pid = $self->child_pid; |
145
|
1
|
|
|
|
|
19
|
Dlog_trace { "Executing on_done handler in future for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
146
|
1
|
|
|
|
|
13
|
$self->_fail_outstanding("Object::Remote connection lost: " . ($f->get)[0]); |
147
|
1
|
50
|
|
|
|
3
|
return unless defined $pid; |
148
|
1
|
|
|
|
|
15
|
log_debug { "Waiting for child '$pid' to exit" }; |
|
0
|
|
|
|
|
0
|
|
149
|
1
|
|
|
|
|
1600
|
my $ret = waitpid($pid, 0); |
150
|
1
|
50
|
|
|
|
10
|
if ($ret != $pid) { |
|
|
50
|
|
|
|
|
|
151
|
0
|
|
|
|
|
0
|
log_debug { "Waited for pid $pid but waitpid() returned $ret" }; |
|
0
|
|
|
|
|
0
|
|
152
|
0
|
|
|
|
|
0
|
return; |
153
|
|
|
|
|
|
|
} elsif ($? & 127) { |
154
|
0
|
|
|
|
|
0
|
log_warn { "Remote interpreter did not exit cleanly" }; |
|
0
|
|
|
|
|
0
|
|
155
|
|
|
|
|
|
|
} else { |
156
|
|
|
|
|
|
|
log_verbose { |
157
|
0
|
|
|
|
|
0
|
my $exit_value = $? >> 8; |
158
|
0
|
|
|
|
|
0
|
"Remote Perl interpreter exited with value '$exit_value'" |
159
|
1
|
|
|
|
|
18
|
}; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
1
|
|
|
|
|
13
|
delete $child_pids{$pid}; |
163
|
19
|
|
|
|
|
362
|
}); |
164
|
19
|
|
|
|
|
1307
|
return $f; |
165
|
|
|
|
|
|
|
}; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub _id_to_remote_object { |
168
|
241
|
|
|
241
|
|
561
|
my ($self, $id) = @_; |
169
|
241
|
|
|
0
|
|
1336
|
Dlog_trace { "fetching proxy for remote object with id '$id' for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
170
|
241
|
100
|
|
|
|
3019
|
return bless({}, 'Object::Remote::Null') if $id eq 'NULL'; |
171
|
|
|
|
|
|
|
( |
172
|
89
|
|
66
|
|
|
2059
|
$self->remote_objects_by_id->{$id} |
173
|
|
|
|
|
|
|
or Object::Remote::Handle->new(connection => $self, id => $id) |
174
|
|
|
|
|
|
|
)->proxy; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub _build__json { |
178
|
19
|
|
|
19
|
|
648
|
weaken(my $self = shift); |
179
|
|
|
|
|
|
|
JSON::PP->new->filter_json_single_key_object( |
180
|
|
|
|
|
|
|
__remote_object__ => sub { |
181
|
41
|
|
|
41
|
|
27146
|
$self->_id_to_remote_object(@_); |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
)->filter_json_single_key_object( |
184
|
|
|
|
|
|
|
__remote_code__ => sub { |
185
|
21
|
|
|
21
|
|
15127
|
my $code_container = $self->_id_to_remote_object(@_); |
186
|
21
|
|
|
|
|
131
|
sub { $code_container->call(@_) }; |
|
21
|
|
|
|
|
349
|
|
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
)->filter_json_single_key_object( |
189
|
|
|
|
|
|
|
__scalar_ref__ => sub { |
190
|
2
|
|
|
2
|
|
1198
|
my $value = shift; |
191
|
2
|
|
|
|
|
6
|
return \$value; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
)->filter_json_single_key_object( |
194
|
|
|
|
|
|
|
__glob_ref__ => sub { |
195
|
4
|
|
|
4
|
|
2460
|
my $glob_container = $self->_id_to_remote_object(@_); |
196
|
4
|
|
|
|
|
24
|
my $handle = Symbol::gensym; |
197
|
4
|
|
|
|
|
240
|
tie *$handle, 'Object::Remote::GlobProxy', $glob_container; |
198
|
4
|
|
|
|
|
15
|
return $handle; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
)->filter_json_single_key_object( |
201
|
|
|
|
|
|
|
__local_object__ => sub { |
202
|
0
|
|
|
0
|
|
0
|
$self->local_objects_by_id->{$_[0]} |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
)->filter_json_single_key_object( |
205
|
|
|
|
|
|
|
__remote_tied_hash__ => sub { |
206
|
1
|
|
|
1
|
|
687
|
my %tied_hash; |
207
|
1
|
|
|
|
|
4
|
tie %tied_hash, 'Object::Remote::Tied', $self->_id_to_remote_object(@_); |
208
|
1
|
|
|
|
|
3
|
return \%tied_hash; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
)->filter_json_single_key_object( |
211
|
|
|
|
|
|
|
__remote_tied_array__ => sub { |
212
|
1
|
|
|
1
|
|
649
|
my @tied_array; |
213
|
1
|
|
|
|
|
4
|
tie @tied_array, 'Object::Remote::Tied', $self->_id_to_remote_object(@_); |
214
|
1
|
|
|
|
|
4
|
return \@tied_array; |
215
|
|
|
|
|
|
|
} |
216
|
19
|
|
|
|
|
566
|
); |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub _load_if_possible { |
220
|
55
|
|
|
55
|
|
117
|
my ($class) = @_; |
221
|
|
|
|
|
|
|
|
222
|
55
|
|
|
|
|
207
|
use_module($class); |
223
|
|
|
|
|
|
|
|
224
|
55
|
50
|
|
|
|
23933
|
if ($@) { |
225
|
0
|
|
|
0
|
|
0
|
log_debug { "Attempt at loading '$class' failed with '$@'" }; |
|
0
|
|
|
|
|
0
|
|
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
BEGIN { |
231
|
11
|
50
|
|
11
|
|
79
|
unshift our @Guess, sub { blessed($_[0]) ? $_[0] : undef }; |
|
18
|
|
|
|
|
104
|
|
232
|
11
|
|
|
|
|
53
|
map _load_if_possible($_), qw( |
233
|
|
|
|
|
|
|
Object::Remote::Connector::Local |
234
|
|
|
|
|
|
|
Object::Remote::Connector::LocalSudo |
235
|
|
|
|
|
|
|
Object::Remote::Connector::SSH |
236
|
|
|
|
|
|
|
Object::Remote::Connector::UNIX |
237
|
|
|
|
|
|
|
Object::Remote::Connector::INET |
238
|
|
|
|
|
|
|
); |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub conn_from_spec { |
242
|
18
|
|
|
18
|
0
|
98
|
my ($class, $spec, @args) = @_; |
243
|
18
|
|
|
|
|
35
|
foreach my $poss (do { our @Guess }) { |
|
18
|
|
|
|
|
117
|
|
244
|
36
|
100
|
|
|
|
145
|
if (my $conn = $poss->($spec, @args)) { |
245
|
18
|
|
|
|
|
68
|
return $conn; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
0
|
|
|
|
|
0
|
return undef; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub new_from_spec { |
253
|
28
|
|
|
28
|
0
|
84
|
my ($class, $spec, @args) = @_; |
254
|
28
|
100
|
|
|
|
140
|
return $spec if blessed $spec; |
255
|
18
|
|
|
|
|
123
|
my $conn = $class->conn_from_spec($spec, @args); |
256
|
|
|
|
|
|
|
|
257
|
18
|
50
|
|
|
|
57
|
die "Couldn't figure out what to do with ${spec}" |
258
|
|
|
|
|
|
|
unless defined $conn; |
259
|
|
|
|
|
|
|
|
260
|
18
|
|
|
|
|
164
|
return $conn->maybe::start::connect; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub remote_object { |
264
|
20
|
|
|
20
|
0
|
184
|
my ($self, @args) = @_; |
265
|
20
|
|
|
|
|
369
|
Object::Remote::Handle->new( |
266
|
|
|
|
|
|
|
connection => $self, @args |
267
|
|
|
|
|
|
|
)->proxy; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub connect { |
271
|
0
|
|
|
0
|
0
|
0
|
my ($self, $to) = @_; |
272
|
0
|
|
|
0
|
|
0
|
Dlog_debug { "Creating connection to remote node '$to' for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
273
|
0
|
|
|
|
|
0
|
return await_future( |
274
|
|
|
|
|
|
|
$self->send_class_call(0, 'Object::Remote', connect => $to) |
275
|
|
|
|
|
|
|
); |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
sub remote_sub { |
279
|
21
|
|
|
21
|
0
|
98
|
my ($self, $sub) = @_; |
280
|
21
|
|
|
|
|
213
|
my ($pkg, $name) = $sub =~ m/^(.*)::([^:]+)$/; |
281
|
21
|
|
|
0
|
|
363
|
Dlog_debug { "Invoking remote sub '$sub' for connection '$_'" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
282
|
21
|
|
|
|
|
339
|
return await_future($self->send_class_call(0, $pkg, can => $name)); |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
sub send_class_call { |
286
|
63
|
|
|
63
|
0
|
268
|
my ($self, $ctx, @call) = @_; |
287
|
63
|
|
|
0
|
|
570
|
Dlog_trace { "Sending a class call for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
288
|
63
|
|
|
|
|
913
|
$self->send(call => class_call_handler => $ctx => call => @call); |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
sub register_class_call_handler { |
292
|
0
|
|
|
0
|
0
|
0
|
my ($self) = @_; |
293
|
0
|
|
0
|
|
|
0
|
$self->local_objects_by_id->{'class_call_handler'} ||= do { |
294
|
0
|
|
|
|
|
0
|
my $o = $self->new_class_call_handler; |
295
|
0
|
|
|
|
|
0
|
$self->_local_object_to_id($o); |
296
|
0
|
|
|
|
|
0
|
$o; |
297
|
|
|
|
|
|
|
}; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
sub new_class_call_handler { |
301
|
|
|
|
|
|
|
Object::Remote::CodeContainer->new( |
302
|
|
|
|
|
|
|
code => sub { |
303
|
0
|
|
|
0
|
|
0
|
my ($class, $method) = (shift, shift); |
304
|
0
|
|
|
|
|
0
|
use_module($class)->$method(@_); |
305
|
|
|
|
|
|
|
} |
306
|
0
|
|
|
0
|
0
|
0
|
); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
sub register_remote { |
310
|
127
|
|
|
127
|
0
|
306
|
my ($self, $remote) = @_; |
311
|
127
|
|
|
0
|
|
894
|
Dlog_trace { my $i = $remote->id; "Registered a remote object with id of '$i' for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
312
|
127
|
|
|
|
|
1955
|
weaken($self->remote_objects_by_id->{$remote->id} = $remote); |
313
|
127
|
|
|
|
|
820
|
return $remote; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub send_free { |
317
|
19
|
|
|
19
|
0
|
59
|
my ($self, $id) = @_; |
318
|
19
|
|
|
0
|
|
259
|
Dlog_trace { "sending request to free object '$id' for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
319
|
|
|
|
|
|
|
#TODO this shows up some times when a remote side dies in the middle of a remote |
320
|
|
|
|
|
|
|
#method invocation - possibly only when the object is being constructed? |
321
|
|
|
|
|
|
|
#(in cleanup) Use of uninitialized value $id in delete at ../Object-Remote/lib/Object/Remote/Connection. |
322
|
19
|
|
|
|
|
324
|
delete $self->remote_objects_by_id->{$id}; |
323
|
19
|
|
|
|
|
198
|
$self->_send([ free => $id ]); |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
sub send { |
327
|
155
|
|
|
155
|
0
|
563
|
my ($self, $type, @call) = @_; |
328
|
|
|
|
|
|
|
|
329
|
155
|
|
|
|
|
526
|
my $future = Future->new; |
330
|
155
|
|
|
|
|
1254
|
my $remote = $self->remote_objects_by_id->{$call[0]}; |
331
|
|
|
|
|
|
|
|
332
|
155
|
|
|
|
|
485
|
unshift @call, $type => $self->_local_object_to_id($future); |
333
|
|
|
|
|
|
|
|
334
|
155
|
|
|
|
|
421
|
my $outstanding = $self->outstanding_futures; |
335
|
155
|
|
|
|
|
565
|
$outstanding->{$future} = $future; |
336
|
|
|
|
|
|
|
$future->on_ready(sub { |
337
|
155
|
|
|
155
|
|
8403
|
undef($remote); |
338
|
155
|
|
|
|
|
833
|
delete $outstanding->{$future} |
339
|
155
|
|
|
|
|
988
|
}); |
340
|
|
|
|
|
|
|
|
341
|
155
|
|
|
|
|
3721
|
$self->_send(\@call); |
342
|
|
|
|
|
|
|
|
343
|
155
|
|
|
|
|
906
|
return $future; |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
sub send_discard { |
347
|
21
|
|
|
21
|
0
|
94
|
my ($self, $type, @call) = @_; |
348
|
|
|
|
|
|
|
|
349
|
21
|
|
|
|
|
87
|
unshift @call, $type => 'NULL'; |
350
|
|
|
|
|
|
|
|
351
|
21
|
|
|
|
|
71
|
$self->_send(\@call); |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
sub _send { |
355
|
195
|
|
|
195
|
|
431
|
my ($self, $to_send) = @_; |
356
|
195
|
|
|
|
|
452
|
my $fh = $self->send_to_fh; |
357
|
|
|
|
|
|
|
|
358
|
195
|
100
|
|
|
|
454
|
unless ($self->is_valid) { |
359
|
1
|
|
|
|
|
226
|
croak "Attempt to invoke _send on a connection that is not valid"; |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
|
362
|
194
|
|
|
0
|
|
1191
|
Dlog_trace { "Starting to serialize data in argument to _send for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
363
|
194
|
|
|
|
|
2132
|
my $serialized = $self->_serialize($to_send)."\n"; |
364
|
194
|
|
|
0
|
|
1199
|
Dlog_trace { my $l = length($serialized); "serialization is completed; sending '$l' characters of serialized data to $_" } $fh; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
365
|
194
|
|
|
|
|
2047
|
my $ret; |
366
|
194
|
|
|
|
|
307
|
eval { |
367
|
|
|
|
|
|
|
#TODO this should be converted over to a non-blocking ::WriteChannel class |
368
|
194
|
50
|
|
|
|
646
|
die "filehandle is not open" unless openhandle($fh); |
369
|
194
|
|
|
0
|
|
874
|
log_trace { "file handle has passed openhandle() test; printing to it" }; |
|
0
|
|
|
|
|
0
|
|
370
|
194
|
|
|
|
|
8616
|
$ret = print $fh $serialized; |
371
|
194
|
50
|
|
|
|
878
|
die "print was not successful: $!" unless defined $ret |
372
|
|
|
|
|
|
|
}; |
373
|
|
|
|
|
|
|
|
374
|
194
|
50
|
|
|
|
441
|
if ($@) { |
375
|
0
|
|
|
0
|
|
0
|
Dlog_debug { "exception encountered when trying to write to file handle $_: $@" } $fh; |
|
0
|
|
|
|
|
0
|
|
376
|
0
|
|
|
|
|
0
|
my $error = $@; |
377
|
0
|
|
|
|
|
0
|
chomp($error); |
378
|
0
|
0
|
|
|
|
0
|
$self->on_close->done("could not write to file handle: $error") unless $self->on_close->is_ready; |
379
|
0
|
|
|
|
|
0
|
return; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
194
|
|
|
|
|
488
|
return $ret; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
sub _serialize { |
386
|
194
|
|
|
194
|
|
406
|
my ($self, $data) = @_; |
387
|
194
|
|
|
|
|
547
|
local our @New_Ids = (-1); |
388
|
|
|
|
|
|
|
return eval { |
389
|
|
|
|
|
|
|
my $flat = $self->_encode($self->_deobjectify($data)); |
390
|
|
|
|
|
|
|
$flat; |
391
|
194
|
|
33
|
|
|
331
|
} || do { |
392
|
|
|
|
|
|
|
my $err = $@; # won't get here if the eval doesn't die |
393
|
|
|
|
|
|
|
# don't keep refs to new things |
394
|
|
|
|
|
|
|
delete @{$self->local_objects_by_id}{@New_Ids}; |
395
|
|
|
|
|
|
|
die "Error serializing: $err"; |
396
|
|
|
|
|
|
|
}; |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub _local_object_to_id { |
400
|
199
|
|
|
199
|
|
4306
|
my ($self, $object) = @_; |
401
|
199
|
|
|
|
|
507
|
my $id = refaddr($object); |
402
|
199
|
|
33
|
|
|
1254
|
$self->local_objects_by_id->{$id} ||= do { |
403
|
199
|
100
|
|
|
|
463
|
push our(@New_Ids), $id if @New_Ids; |
404
|
199
|
|
|
|
|
598
|
$object; |
405
|
|
|
|
|
|
|
}; |
406
|
199
|
|
|
|
|
982
|
return $id; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
sub _deobjectify { |
410
|
1444
|
|
|
1444
|
|
2090
|
my ($self, $data) = @_; |
411
|
1444
|
100
|
|
|
|
3307
|
if (blessed($data)) { |
|
|
100
|
|
|
|
|
|
412
|
39
|
100
|
66
|
|
|
451
|
if ( |
413
|
|
|
|
|
|
|
$data->isa('Object::Remote::Proxy') |
414
|
|
|
|
|
|
|
and $data->{remote}->connection == $self |
415
|
|
|
|
|
|
|
) { |
416
|
1
|
|
|
|
|
27
|
return +{ __local_object__ => $data->{remote}->id }; |
417
|
|
|
|
|
|
|
} else { |
418
|
38
|
|
|
|
|
120
|
return +{ __remote_object__ => $self->_local_object_to_id($data) }; |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
} elsif (my $ref = ref($data)) { |
421
|
205
|
100
|
|
|
|
673
|
if ($ref eq 'HASH') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
422
|
3
|
|
|
|
|
5
|
my $tied_to = tied(%$data); |
423
|
3
|
50
|
|
|
|
6
|
if(defined($tied_to)) { |
424
|
0
|
|
|
|
|
0
|
return +{__remote_tied_hash__ => $self->_local_object_to_id($tied_to)}; |
425
|
|
|
|
|
|
|
} else { |
426
|
3
|
|
|
|
|
14
|
return +{ map +($_ => $self->_deobjectify($data->{$_})), keys %$data }; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
} elsif ($ref eq 'ARRAY') { |
429
|
194
|
|
|
|
|
369
|
my $tied_to = tied(@$data); |
430
|
194
|
50
|
|
|
|
388
|
if (defined($tied_to)) { |
431
|
0
|
|
|
|
|
0
|
return +{__remote_tied_array__ => $self->_local_object_to_id($tied_to)}; |
432
|
|
|
|
|
|
|
} else { |
433
|
194
|
|
|
|
|
581
|
return [ map $self->_deobjectify($_), @$data ]; |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
} elsif ($ref eq 'CODE') { |
436
|
5
|
|
|
|
|
123
|
my $id = $self->_local_object_to_id( |
437
|
|
|
|
|
|
|
Object::Remote::CodeContainer->new(code => $data) |
438
|
|
|
|
|
|
|
); |
439
|
5
|
|
|
|
|
100
|
return +{ __remote_code__ => $id }; |
440
|
|
|
|
|
|
|
} elsif ($ref eq 'SCALAR') { |
441
|
2
|
|
|
|
|
50
|
return +{ __scalar_ref__ => $$data }; |
442
|
|
|
|
|
|
|
} elsif ($ref eq 'GLOB') { |
443
|
1
|
|
|
|
|
42
|
return +{ __glob_ref__ => $self->_local_object_to_id( |
444
|
|
|
|
|
|
|
Object::Remote::GlobContainer->new(handle => $data) |
445
|
|
|
|
|
|
|
) }; |
446
|
|
|
|
|
|
|
} else { |
447
|
0
|
|
|
|
|
0
|
die "Can't collapse reftype $ref"; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
} |
450
|
1200
|
|
|
|
|
5105
|
return $data; # plain scalar |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
sub _receive { |
454
|
173
|
|
|
173
|
|
427
|
my ($self, $flat) = @_; |
455
|
173
|
|
|
0
|
|
1202
|
Dlog_trace { my $l = length($flat); "Starting to deserialize $l characters of data for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
456
|
173
|
|
|
|
|
285
|
my ($type, @rest) = eval { @{$self->_deserialize($flat)} } |
|
173
|
|
|
|
|
2644
|
|
457
|
173
|
50
|
|
|
|
1932
|
or do { warn "Deserialize failed for ${flat}: $@"; return }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
458
|
173
|
|
|
0
|
|
58731
|
Dlog_trace { "deserialization complete for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
459
|
173
|
|
|
|
|
284
|
eval { $self->${\"receive_${type}"}(@rest); 1 } |
|
173
|
|
|
|
|
1116
|
|
|
173
|
|
|
|
|
865
|
|
460
|
173
|
50
|
|
|
|
1895
|
or do { warn "Receive failed for ${flat}: $@"; return }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
461
|
173
|
|
|
|
|
1553
|
return; |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
sub receive_free { |
465
|
152
|
|
|
152
|
0
|
396
|
my ($self, $id) = @_; |
466
|
152
|
|
|
0
|
|
970
|
Dlog_trace { "got a receive_free for object '$id' for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
467
|
152
|
50
|
|
|
|
1876
|
delete $self->local_objects_by_id->{$id} |
468
|
|
|
|
|
|
|
or warn "Free: no such object $id"; |
469
|
152
|
|
|
|
|
421
|
return; |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
sub receive_call { |
473
|
173
|
|
|
173
|
0
|
516
|
my ($self, $future_id, $id, @rest) = @_; |
474
|
173
|
|
|
0
|
|
1019
|
Dlog_trace { "got a receive_call for object '$id' for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
475
|
173
|
|
|
|
|
1871
|
my $future = $self->_id_to_remote_object($future_id); |
476
|
173
|
|
|
|
|
477
|
$future->{method} = 'call_discard_free'; |
477
|
|
|
|
|
|
|
my $local = $self->local_objects_by_id->{$id} |
478
|
173
|
50
|
|
|
|
698
|
or do { $future->fail("No such object $id"); return }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
479
|
173
|
|
|
|
|
473
|
$self->_invoke($future, $local, @rest); |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub receive_call_free { |
483
|
152
|
|
|
152
|
0
|
454
|
my ($self, $future, $id, @rest) = @_; |
484
|
152
|
|
|
0
|
|
964
|
Dlog_trace { "got a receive_call_free for object '$id' for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
485
|
152
|
|
|
|
|
1791
|
$self->receive_call($future, $id, undef, @rest); |
486
|
152
|
|
|
|
|
523
|
$self->receive_free($id); |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
sub _invoke { |
490
|
173
|
|
|
173
|
|
498
|
my ($self, $future, $local, $ctx, $method, @args) = @_; |
491
|
173
|
|
|
0
|
|
989
|
Dlog_trace { "got _invoke for a method named '$method' for connection $_" } $self->_id; |
|
0
|
|
|
|
|
0
|
|
492
|
173
|
50
|
|
|
|
1832
|
if ($method =~ /^start::/) { |
493
|
0
|
|
|
|
|
0
|
my $f = $local->$method(@args); |
494
|
0
|
|
|
0
|
|
0
|
$f->on_done(sub { undef($f); $future->done(@_) }); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
495
|
0
|
0
|
|
|
|
0
|
return unless $f; |
496
|
0
|
|
|
0
|
|
0
|
$f->on_fail(sub { undef($f); $future->fail(@_) }); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
497
|
0
|
|
|
|
|
0
|
return; |
498
|
|
|
|
|
|
|
} |
499
|
173
|
|
|
173
|
|
665
|
my $do = sub { $local->$method(@args) }; |
|
173
|
|
|
|
|
971
|
|
500
|
|
|
|
|
|
|
eval { |
501
|
|
|
|
|
|
|
$future->done( |
502
|
|
|
|
|
|
|
defined($ctx) |
503
|
|
|
|
|
|
|
? ($ctx ? $do->() : scalar($do->())) |
504
|
173
|
50
|
|
|
|
442
|
: do { $do->(); () } |
|
157
|
100
|
|
|
|
369
|
|
|
157
|
|
|
|
|
4003706
|
|
505
|
|
|
|
|
|
|
); |
506
|
172
|
|
|
|
|
496
|
1; |
507
|
173
|
100
|
|
|
|
342
|
} or do { $future->fail($@); return; }; |
|
1
|
|
|
|
|
34
|
|
|
1
|
|
|
|
|
24
|
|
508
|
172
|
|
|
|
|
1152
|
return; |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
1; |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=head1 NAME |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
Object::Remote::Connection - An underlying connection for L |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
use Object::Remote; |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
my $local = Object::Remote->connect('-'); |
520
|
|
|
|
|
|
|
my $remote = Object::Remote->connect('myserver'); |
521
|
|
|
|
|
|
|
my $remote_user = Object::Remote->connect('user@myserver'); |
522
|
|
|
|
|
|
|
my $local_sudo = Object::Remote->connect('user@'); |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
#$remote can be any other connection object |
525
|
|
|
|
|
|
|
my $hostname = Sys::Hostname->can::on($remote, 'hostname'); |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=head1 DESCRIPTION |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
This is the class that supports connections to remote objects. |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
=head1 SEE ALSO |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=over 4 |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
=item C |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=item C |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=back |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
=cut |