From cfd20b04dfe495df80ad9c62949103d555f65f49 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 25 Mar 2023 02:08:52 +0000 Subject: ipc: retry sendmsg + recvmsg calls on EINTR I'm not sure how this went undetected for so long, but EINTR must be checked for when working with blocking sockets. EINTR shouldn't happen for non-blocking sockets, though, but it's easier to just use the new wrapper in most of those places. I don't know what I was smoking when I left out EINTR checks :x --- lib/PublicInbox/IPC.pm | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'lib/PublicInbox/IPC.pm') diff --git a/lib/PublicInbox/IPC.pm b/lib/PublicInbox/IPC.pm index 548a72eb..7fa656d0 100644 --- a/lib/PublicInbox/IPC.pm +++ b/lib/PublicInbox/IPC.pm @@ -216,9 +216,27 @@ sub ipc_sibling_atfork_child { $pid == $$ and die "BUG: $$ ipc_atfork_child called on itself"; } +sub send_cmd ($$$$) { + my ($s, $fds, $buf, $fl) = @_; + while (1) { + my $n = $send_cmd->($s, $fds, $buf, $fl); + next if !defined($n) && $!{EINTR}; + return $n; + } +} + +sub recv_cmd ($$$) { + my ($s, undef, $len) = @_; # $_[1] is $buf + while (1) { + my @fds = $recv_cmd->($s, $_[1], $len); + next if scalar(@fds) == 1 && !defined($fds[0]) && $!{EINTR}; + return @fds; + } +} + sub recv_and_run { my ($self, $s2, $len, $full_stream) = @_; - my @fds = $recv_cmd->($s2, my $buf, $len // $MY_MAX_ARG_STRLEN); + my @fds = recv_cmd($s2, my $buf, $len // $MY_MAX_ARG_STRLEN); return if scalar(@fds) && !defined($fds[0]); my $n = length($buf) or return 0; my $nfd = 0; @@ -278,15 +296,18 @@ sub stream_in_full ($$$) { my ($s1, $fds, $buf) = @_; socketpair(my $r, my $w, AF_UNIX, SOCK_STREAM, 0) or croak "socketpair: $!"; - my $n = $send_cmd->($s1, [ fileno($r) ], + my $n = send_cmd($s1, [ fileno($r) ], ipc_freeze(['do_sock_stream', length($buf)]), MSG_EOR) // croak "sendmsg: $!"; undef $r; - $n = $send_cmd->($w, $fds, $buf, 0) // croak "sendmsg: $!"; + $n = send_cmd($w, $fds, $buf, 0) // croak "sendmsg: $!"; while ($n < length($buf)) { - my $x = syswrite($w, $buf, length($buf) - $n, $n) // - croak "syswrite: $!"; - croak "syswrite wrote 0 bytes" if $x == 0; + my $x = syswrite($w, $buf, length($buf) - $n, $n); + if (!defined($n)) { + next if $!{EINTR}; + croak "syswrite: $!"; + } + $x or croak "syswrite wrote 0 bytes"; $n += $x; } } @@ -299,7 +320,7 @@ sub wq_io_do { # always async if (length($buf) > $MY_MAX_ARG_STRLEN) { stream_in_full($s1, $fds, $buf); } else { - my $n = $send_cmd->($s1, $fds, $buf, MSG_EOR); + my $n = send_cmd $s1, $fds, $buf, MSG_EOR; return if defined($n); # likely $!{ETOOMANYREFS} and croak "sendmsg: $! (check RLIMIT_NOFILE)"; -- cgit v1.2.3-24-ge0c7