Add retries on signal interrupt to the splicing into and out of the intermediate input pipe, and add a TODO marker highlighting a problem with the way splice() is currently handled.

This commit is contained in:
Andrew Wood
2026-05-13 22:19:03 +01:00
parent d65dbd2f37
commit 60203aabf5
+52 -7
View File
@@ -295,6 +295,40 @@ static ssize_t pv__transfer__write_repeated(int fd, char *buf, size_t count, boo
}
#ifdef HAVE_SPLICE
/*
* Call splice(), retrying several times with a small pause if interrupted
* by a signal.
*/
static inline ssize_t retrying_splice(int fd_in, /*@null@ */ off_t *off_in, int fd_out, /*@null@ */ off_t *off_out,
size_t len, unsigned int flags)
{
ssize_t result = -1;
int attempt;
/*@-nullpass@ */
/*@-type@ */
/* splint doesn't know about splice. */
for (attempt = 0; attempt < 10; attempt++) {
if (attempt > 0) {
debug("%s(%d->%d): %s: %s", "splice", fd_in, fd_out, "transient error - waiting briefly",
strerror(errno));
(void) is_data_ready(-1, NULL, -1, NULL, 1000);
}
result = splice(fd_in, off_in, fd_out, off_out, len, flags);
if ((result >= 0) || ((EINTR != errno) && (EAGAIN != errno)))
break;
}
/*@+type@ */
/*@+nullpass@ */
return result;
}
#endif /* HAVE_SPLICE */
/*
* Read some data from the given file descriptor, updating the state.
*
@@ -423,12 +457,13 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
ssize_t spliced_in;
spliced_in =
splice(fd, NULL, state->transfer.intermediate_pipe[1], NULL, bytes_to_splice_in,
SPLICE_F_MORE);
retrying_splice(fd, NULL, state->transfer.intermediate_pipe[1], NULL,
bytes_to_splice_in, SPLICE_F_MORE);
if (spliced_in > 0) {
state->transfer.intermediate_pipe_buffer_used += (int) spliced_in;
}
nread = spliced_in;
debug("%s(%d->%d): %ld", "spliced_in", fd, state->transfer.intermediate_pipe[1], spliced_in);
}
/*
@@ -444,12 +479,13 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
bytes_to_splice_out = bytes_to_splice;
spliced_out =
splice(state->transfer.intermediate_pipe[0], NULL, output_fd, NULL,
bytes_to_splice_out, SPLICE_F_MORE);
retrying_splice(state->transfer.intermediate_pipe[0], NULL, output_fd, NULL,
bytes_to_splice_out, SPLICE_F_MORE);
if (spliced_out > 0) {
state->transfer.intermediate_pipe_buffer_used -= (int) spliced_out;
}
nread = spliced_out;
debug("%s(%d->%d): %ld", "spliced_out", state->transfer.intermediate_pipe[0], output_fd, spliced_out);
}
} else {
/* Normal splice() from input to output. */
@@ -489,11 +525,20 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
}
#endif /* HAVE_FDATASYNC */
} else if ((-1 == nread) && (EAGAIN == errno)) {
/* nothing read yet - do nothing. */
/* signal interrupt - do nothing. */
} else if (0 == nread) {
/*
* nothing read, turn off splice() for this fd and
* fall back to read, but not that causes blocking
* when ^D is pressed on stdin such that two ^Ds are
* needed; TODO: rework this whole section into its
* own function like pv__transfer__read_repeated(),
* using is_data_ready().
*/
state->transfer.splice_used = false;
} else {
/*
* Some other error, or splice() returned 0 - stop
* using splice() on this fd.
* Unknown error - stop using splice() on this fd.
*/
state->transfer.splice_used = false;
}