src/pv/transfer.c: timer going off returns with total_written==0; which is not an error. #92
No reviewers
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ivarch/pv!92
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "eborisch/pv:write-can-return-0"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
UPDATED:
The issue is not write(2) returning 0; I thought I had that pinned down in the debugger, but alas, PEBKAC. write(2) is returning -1, with errno == EINTR.
The full error path is:
a. This blocks (on FreeBSD -- likely only for some/small sizes). If the timer expires before it is able to finish writing (due to the downstream reader process not fetching new data for over a second), -1/EINTR will result.
After receiving nwritten < 0 and handling EINTR (line 231), it returns 0 (total_written), and pv__transfer_write takes this as a reason to stop writing to stdout.
On Linux, in (2) it completes the partial write to top-off the buffer, and then returns EAGAIN on the attempted follow-up, so total_written will be the partial (>0) value. So I don't think you'll hit this specific set of conditions on linux.
The fix remains the same here; this is a transient / self-inflicted EINTR, don't stop attempting to write.
I'm not sure where this "on output EOF" concept is coming from -- that might be a misreading of the man page. (I think the man page is suggesting that a "writer can request a write of size 0 (returns 0)" to signal to a pipe listener that it is the end of the file (read returns 0) without SIGPIPE ?)
You can reproduce this on FreeBSD with the following:
pv < /dev/zero | { while sleep 1.1; do dd bs=63k count=1 of=/dev/null; done ; }
1+0 records in1 [62.1KiB/s] [ <=> ]
1+0 records out
64512 bytes transferred in 0.000093 secs (690647482 bytes/sec)
0+1 records in2 [0.00 B/s] [ <=> ]
0+1 records out
1024 bytes transferred in 0.000049 secs (21070392 bytes/sec)
64.0KiB 0:00:10 [0.00 B/s] [ <=>
[ End of output ]
Change the sleep to 0.9, for example, and it just keeps running.
At least, it's not an error so far as I can see in the man 2 write on freebsd:
RETURN VALUES
Upon successful completion the number of bytes which were written is
returned. Otherwise a -1 is returned and the global variable errno is
set to indicate the error.
Some combination of the timers that are being configured, writing to a pipe, etc. is generating a 0 return when there isn't an error. See the discussion here (https://lists.freebsd.org/archives/freebsd-stable/2024-July/002282.html) to see if others decide this is, in fact, a bug in FreeBSD instead.
src/pv/transfer.c: write(2) returning 0 is not an errorto src/pv/transfer.c: write(2) returning 0 is not (?) an errorHello
Thanks for this.
The manual for write(2) suggests that it returning zero has different semantics to read(2) returning zero and PV's code doesn't seem to reflect that at first glance. The manual on Debian, at least. Certainly PV's code talks about "EOF on write" which maybe isn't really a thing as far as I can tell, so that's confusing at best.
It may be that Linux and FreeBSD behave differently here.
When you apply your changes, does "
make check" still work?Can you provide any examples of this behaviour without recourse to zfs, such that we could put it into a test that will run on multiple platforms (without admin rights)?
I’ve tried (unsuccessfully so far) to create a synthetic test that aggravates this issue.
On the mailing list I linked to I’ve described the problem and posed the “should write(2) ever return 0 (when asked to write >0 bytes)?” question. (The FreeBSD manual page on the subject mainly says that on error the return will be -1.)
On FreeBSD 14-1-RELEASE-p1 with the patch, all 38 non-skipped (3 Memory_safety tests) tests pass.
I'm curious if this may be related to #87 as well as the non-blocking issue with DD?
It's good to know the current tests still pass with the changes in place.
That sounds like an interesting avenue to explore - it certainly sounds plausible. Thanks.
src/pv/transfer.c: write(2) returning 0 is not (?) an errorto src/pv/transfer.c: timer going off returns with total_written==0; which is not an error.Thanks again for this. It has now been merged, with a few small changes applied afterwards.
It looks like the #87 issue is not related as that seems to involve signals not interrupting writes rather than early exits.
I've updated the overall description at the top to better describe what is happening.