From 2baa916e55b234aefa7ac4c9060f91937dea6fe3 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 7 Sep 2025 23:41:44 +0000 Subject: [PATCH] ipc: improve exception handling When an exception triggers a teardown of the worker process (ipc_worker_stop), we need to combine subsequent exceptions and show the original one, first. In other words, we must not lose the original exception if new exceptions are thrown during teardown. So rely on `wantarray' to grab caller contexts to allow returning exceptions as a list rather than throwing them immediately. --- lib/PublicInbox/IPC.pm | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/PublicInbox/IPC.pm b/lib/PublicInbox/IPC.pm index 257c78677..6f380d4c8 100644 --- a/lib/PublicInbox/IPC.pm +++ b/lib/PublicInbox/IPC.pm @@ -66,8 +66,8 @@ sub _get_rec ($) { sub ipc_fail ($@) { my ($self, @msg) = @_; - eval { ipc_worker_stop($self) }; - unshift @msg, " (stop: $@)" if $@; + my @err = eval { ipc_worker_stop($self) }; + unshift @msg, @err; eval { delete $self->{-ipc_res} }; unshift @msg, " (delete -ipc_res: $@)" if $@; croak @msg; @@ -91,6 +91,7 @@ sub ipc_read_step ($$) { my $ret = ipc_get_res $self; splice @$inflight, 0, 4; eval { $acb->($self, $sub, $sub_arg, $acb_arg, $ret) }; + return ($@ ? ($@) : ()) if wantarray; ipc_fail $self, "E: $sub $@" if $@; } @@ -236,9 +237,13 @@ sub ipc_worker_stop { my ($self) = @_; if (my $w_req = delete $self->{-ipc_req}) { close $w_req; # invalidate if referenced upstack - ipc_wait_all $self; - delete $self->{-ipc_res}; # ipc_worker_reap will fire + my @exc = ipc_wait_all $self; + my $res = delete $self->{-ipc_res}; + return @exc if wantarray; + die @exc if @exc; + # ipc_worker_reap will fire for $res going out-of-scope } + (); } sub _wait_return ($$) { @@ -251,15 +256,22 @@ sub _wait_return ($$) { my $ipc_die = sub { # default ipc_async acb my ($self, undef, undef, undef, $ret) = @_; if (ref($ret) eq 'PublicInbox::IPC::Die') { - ipc_worker_stop $self; - croak $$ret; + my @err = ("$$ret"); + push @err, (eval { ipc_worker_stop $self }); + push @err, $@ if $@; + die @err; } }; sub ipc_wait_all ($) { my ($self) = @_; - my $inflight = $self->{-ipc_inflight} // return; - ipc_read_step($self, $inflight) while @$inflight; + my @exc; + my $inflight = $self->{-ipc_inflight} // return @exc; + while (@$inflight) { + push @exc, ipc_read_step($self, $inflight); + } + croak(@exc) if @exc && !wantarray; + @exc; } # call $self->$sub(@args), on a worker if ipc_worker_spawn was used -- 2.47.3