From 8d03238d630004bd7c7d9afa925227591ddb1a88 Mon Sep 17 00:00:00 2001 From: Aleksey Mashanov <aleksey.mashanov@gmail.com> Date: Mon, 12 Dec 2011 12:20:51 +0400 Subject: [PATCH] Fix loss of $! in case of $SIG{__DIE__} usage --- mod/silverbox/client/perl/lib/MR/IProto.pm | 26 +++++++++++++------ .../perl/lib/MR/IProto/Connection/Async.pm | 5 ++-- .../perl/lib/MR/IProto/Connection/Sync.pm | 9 ++++--- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/mod/silverbox/client/perl/lib/MR/IProto.pm b/mod/silverbox/client/perl/lib/MR/IProto.pm index 39f07d233b..3f0ce214b7 100644 --- a/mod/silverbox/client/perl/lib/MR/IProto.pm +++ b/mod/silverbox/client/perl/lib/MR/IProto.pm @@ -27,8 +27,7 @@ Or without it: servers => 'xxx.xxx.xxx.xxx:xxxx,xxx.xxx.xxx.xxx:xxxx', ); -Messages can be prepared and processed using objects (recomended, -but requires some more CPU): +Messages can be prepared and processed using objects (requires some more CPU): my $request = MyProject::Message::MyOperation::Request->new( arg1 => 1, @@ -39,7 +38,7 @@ but requires some more CPU): # Of course, both message classes (request and reply) must # be implemented by user. -Or without them (not recommended because of unclean architecture): +Or without them: my $response = $client->send({ msg => x, @@ -230,6 +229,7 @@ sub send { } else { die "Method must be called in scalar context if you want to use sync" unless defined wantarray; + local $SIG{__DIE__} = do { my $old = $SIG{__DIE__}; sub { local $! = 0; $old->(@_); } } if ref $SIG{__DIE__}; my %servers; my ($data, $error, $errno); $self->_send_now($message, sub { @@ -281,6 +281,7 @@ sub send_bulk { } else { die "Method must be called in scalar context if you want to use sync" unless defined wantarray; + local $SIG{__DIE__} = do { my $old = $SIG{__DIE__}; sub { local $! = 0; $old->(@_); } } if ref $SIG{__DIE__}; my %servers; foreach my $message ( @$messages ) { $self->_send_now($message, sub { @@ -455,6 +456,11 @@ Message have no reply. Is retry is allowed. Values of attributes L</max_request_retries> and L</retry_delay> is used if retry is allowed. +=item is_retry + +Callback used to determine if server asks for retry. Unpacked data is passed +to it as a first argument. + =back =cut @@ -559,9 +565,11 @@ sub _send_retry { sub _server_callback { my ($self, $req_args, $resp_args) = @_; my ($handler, $args, $callback, $sync, $try) = @$req_args; - my ($resp_msg, $data, $error) = @$resp_args; + my ($resp_msg, $data, $error, $errno) = @$resp_args; eval { if ($error) { + $! = $errno; + $@ = $error; my $retry = defined $args->{request} ? $args->{request}->retry() : ref $args->{retry} eq 'CODE' ? $args->{retry}->() : $args->{retry}; @@ -571,7 +579,7 @@ sub _server_callback { } else { undef $$handler; - $self->_report_error($args->{request}, $callback, $error, $sync); + $self->_report_error($args->{request}, $callback, $error, $sync, $errno); } } else { @@ -622,16 +630,18 @@ sub _recv_now { } sub _report_error { - my ($self, $request, $callback, $error, $sync) = @_; + my ($self, $request, $callback, $error, $sync, $errno) = @_; my $errobj = defined $request && ref $request ne 'HASH' ? MR::IProto::Error->new( request => $request, error => $error, - errno => 0+$!, + errno => defined $errno ? 0 + $errno : 0, ) : undef; $self->_finish_and_start() unless $sync; - $callback->($errobj, $error); + $! = $errno; + $@ = $error; + $callback->($errobj, $error, $errno); return; } diff --git a/mod/silverbox/client/perl/lib/MR/IProto/Connection/Async.pm b/mod/silverbox/client/perl/lib/MR/IProto/Connection/Async.pm index 6f362deebb..d78b9180ff 100644 --- a/mod/silverbox/client/perl/lib/MR/IProto/Connection/Async.pm +++ b/mod/silverbox/client/perl/lib/MR/IProto/Connection/Async.pm @@ -192,10 +192,11 @@ sub _build__handle { }, on_error => sub { my ($handle, $fatal, $message) = @_; + my $errno = $!; $server->_debug(($fatal ? 'fatal ' : '') . 'error: ' . $message); my @callbacks; foreach my $sync ( keys %{$self->_callbacks} ) { - $server->_recv_finished($sync, undef, undef, $message); + $server->_recv_finished($sync, undef, undef, $message, $errno); $self->_in_progress( $self->_in_progress - 1 ); push @callbacks, $self->_callbacks->{$sync}; } @@ -206,7 +207,7 @@ sub _build__handle { $server->_debug('closing socket') if $server->debug >= 1; $handle->destroy(); $self->_try_to_send(); - $_->(undef, undef, $message) foreach @callbacks; + $_->(undef, undef, $message, $errno) foreach @callbacks; return; }, on_timeout => sub { diff --git a/mod/silverbox/client/perl/lib/MR/IProto/Connection/Sync.pm b/mod/silverbox/client/perl/lib/MR/IProto/Connection/Sync.pm index 8328cc7fa8..7c3da90c46 100644 --- a/mod/silverbox/client/perl/lib/MR/IProto/Connection/Sync.pm +++ b/mod/silverbox/client/perl/lib/MR/IProto/Connection/Sync.pm @@ -206,6 +206,7 @@ sub _set_timeout { sub _handle_error { my ($self, $sync, $callback, $error) = @_; + my $errno = $!; if (!$error) { $error = 'Unknown error'; } elsif ($error =~ /^(.+?) at \S+ line \d+/s) { @@ -220,12 +221,12 @@ sub _handle_error { $server->active(0); my $sent = $self->_sent; my @sent = splice @$sent, 0, scalar @$sent; - $server->_recv_finished($sync, undef, undef, $error); - $callback->(undef, undef, $error); + $server->_recv_finished($sync, undef, undef, $error, $errno); + $callback->(undef, undef, $error, $errno); foreach my $args (@sent) { my ($sync, $callback) = @$args; - $server->_recv_finished($sync, undef, undef, $error); - $callback->(undef, undef, $error); + $server->_recv_finished($sync, undef, undef, $error, $errno); + $callback->(undef, undef, $error, $errno); } return } -- GitLab