io-util: introduce loop_write_full that takes a timeout

Also drop do_poll as the use case is covered
by timeout.
This commit is contained in:
Mike Yuan
2023-09-07 20:30:44 +08:00
parent 82b7bf8c1c
commit e22c60a9d5
34 changed files with 103 additions and 81 deletions
+29 -10
View File
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <unistd.h>
#include "errno-util.h"
#include "io-util.h"
#include "string-util.h"
#include "time-util.h"
@@ -105,18 +106,19 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
return 0;
}
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) {
const uint8_t *p;
usec_t end;
int r;
assert(fd >= 0);
assert(buf || nbytes == 0);
if (nbytes == 0) {
static const dummy_t dummy[0];
assert_cc(sizeof(dummy) == 0);
p = (const void*) dummy; /* Some valid pointer, in case NULL was specified */
} else {
assert(buf);
if (nbytes == SIZE_MAX)
nbytes = strlen(buf);
else if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
@@ -125,6 +127,9 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
p = buf;
}
/* When timeout is 0 or USEC_INFINITY this is not used. But we initialize it to a sensible value. */
end = timestamp_is_set(timeout) ? usec_add(now(CLOCK_MONOTONIC), timeout) : USEC_INFINITY;
do {
ssize_t k;
@@ -133,16 +138,30 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
if (errno == EINTR)
continue;
if (errno == EAGAIN && do_poll) {
/* We knowingly ignore any return value here,
* and expect that any error/EOF is reported
* via write() */
if (errno != EAGAIN || timeout == 0)
return -errno;
(void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
continue;
usec_t wait_for;
if (timeout == USEC_INFINITY)
wait_for = USEC_INFINITY;
else {
usec_t t = now(CLOCK_MONOTONIC);
if (t >= end)
return -ETIME;
wait_for = usec_sub_unsigned(end, t);
}
return -errno;
r = fd_wait_for_event(fd, POLLOUT, wait_for);
if (timeout == USEC_INFINITY || ERRNO_IS_NEG_TRANSIENT(r))
/* If timeout == USEC_INFINITY we knowingly ignore any return value
* here, and expect that any error/EOF is reported via write() */
continue;
if (r < 0)
return r;
if (r == 0)
return -ETIME;
}
if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */