Correct issues #34, #86, #87 - progress output stopping when writing output data blocks - by adding a SIGALRM handler, since ignoring the signal prevents it from interrupting writes; and use setitimer() where available, instead of alarm(), to provide sufficient granularity to handle the case where the --interval is not a whole number of seconds.
This commit is contained in:
@@ -29,6 +29,7 @@ AC_CHECK_FUNCS([vsnprintf strlcat strtoul memrchr])
|
||||
AC_CHECK_FUNCS([fdatasync])
|
||||
AC_CHECK_FUNCS([fpathconf sysconf posix_memalign posix_fadvise])
|
||||
AC_CHECK_FUNCS([nanosleep])
|
||||
AC_CHECK_FUNCS([setitimer])
|
||||
AC_CHECK_HEADERS([getopt.h])
|
||||
AC_CHECK_HEADERS([limits.h])
|
||||
AC_CHECK_HEADERS([wctype.h])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
### NOT YET RELEASED
|
||||
|
||||
* feature: new "`--si`" option to display and interpret size suffixes in multiples of 1000 rather than 1024 (pull request [#85](https://codeberg.org/a-j-wood/pv/pulls/85)) supplied by [kevinruddy](https://codeberg.org/kevinruddy)
|
||||
* fix: continue producing progress output when the output is blocking writes ([#34](https://codeberg.org/a-j-wood/pv/issues/34), [#86](https://codeberg.org/a-j-wood/pv/issues/86), [#87](https://codeberg.org/a-j-wood/pv/issues/87))
|
||||
* fix: honour the _TMPDIR_ / _TMP_ environment variables again, rather than hard-coding "`/tmp`", when using a terminal lock file (originally removed in 1.8.0) ([#88](https://codeberg.org/a-j-wood/pv/issues/88))
|
||||
* i18n: corrections and missing strings added to French translations (pull request [#83](https://codeberg.org/a-j-wood/pv/pulls/83)) supplied by [Thomas Bertels](https://codeberg.org/tbertels)
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ Things still to do. (#n) indicates the issue tracker number.
|
||||
* ([#17](https://codeberg.org/a-j-wood/pv/issues/17)) Allow "`-r`" with "`-l`" and "`-n`" to output lines/sec (Roland Kletzing)
|
||||
* ([#22](https://codeberg.org/a-j-wood/pv/issues/22)) Options to skip input and seek on output (Jason A. Pfeil, Feb 2022)
|
||||
* ([#25](https://codeberg.org/a-j-wood/pv/issues/25)) Normalise progress to 100% on overrun (Andrej Gantvorg)
|
||||
* ([#34](https://codeberg.org/a-j-wood/pv/issues/34)) Continue timer even if input or output is blocking (Martin Probst - Jun 2017)
|
||||
* ([#35](https://codeberg.org/a-j-wood/pv/issues/35)) Allow decimal values for "`-s`", "`-L`", "`-B`" (Thomas Watson - Aug 2020)
|
||||
* ([#38](https://codeberg.org/a-j-wood/pv/issues/38)) Reset ETA on *SIGUSR1* (Jacek Wielemborek - Jan 2019)
|
||||
* ([#40](https://codeberg.org/a-j-wood/pv/issues/40)) Permit "`-c`" with "`-d PID:FD`", reject "`-N`" with "`-d PID`" (Norman Rasmussen - Nov 2020)
|
||||
|
||||
@@ -100,6 +100,9 @@
|
||||
/* Define to 1 if you have the `select' function. */
|
||||
#undef HAVE_SELECT
|
||||
|
||||
/* Define to 1 if you have the `setitimer' function. */
|
||||
#undef HAVE_SETITIMER
|
||||
|
||||
/* Define to 1 if you have the `setlocale' function. */
|
||||
#undef HAVE_SETLOCALE
|
||||
|
||||
|
||||
@@ -157,6 +157,7 @@ struct pvstate_s {
|
||||
#ifdef SA_SIGINFO
|
||||
struct sigaction old_sigusr2;
|
||||
#endif
|
||||
struct sigaction old_sigalrm;
|
||||
} signal;
|
||||
|
||||
/*******************
|
||||
|
||||
@@ -244,6 +244,22 @@ bool pv_sigusr2_received(pvstate_t state, pid_t * pid)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Handle alarm signals by doing nothing.
|
||||
*
|
||||
* Note that we have to use a signal handler like this, instead of using
|
||||
* SIG_IGN, because if we ignore the signal entirely, it does nothing,
|
||||
* including not interrupting blocking write() calls - which is what we're
|
||||
* using alarm signals for in the first place.
|
||||
*/
|
||||
static void pv_sig_alrm( /*@unused@ */ __attribute__((unused))
|
||||
int s)
|
||||
{
|
||||
debug("%s", "SIGALRM received");
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialise signal handling.
|
||||
*/
|
||||
@@ -360,6 +376,15 @@ void pv_sig_init(pvstate_t state)
|
||||
* while backgrounded (see the SIGTTOU handler above).
|
||||
*/
|
||||
pv_sig_ensure_tty_tostop();
|
||||
|
||||
/*
|
||||
* Handle SIGALRM by doing nothing, so we can use alarms or interval
|
||||
* timers to interrupt blocking writes (returning EINTR).
|
||||
*/
|
||||
sa.sa_handler = pv_sig_alrm;
|
||||
(void) sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
(void) sigaction(SIGALRM, &sa, &(pv_sig_state->signal.old_sigalrm));
|
||||
}
|
||||
|
||||
|
||||
@@ -389,6 +414,7 @@ void pv_sig_fini( /*@unused@ */ __attribute__((unused)) pvstate_t state)
|
||||
#ifdef SA_SIGINFO
|
||||
(void) sigaction(SIGUSR2, &(pv_sig_state->signal.old_sigusr2), NULL);
|
||||
#endif
|
||||
(void) sigaction(SIGALRM, &(pv_sig_state->signal.old_sigalrm), NULL);
|
||||
|
||||
need_to_clear_tostop = pv_sig_state->signal.pv_tty_tostop_added;
|
||||
|
||||
|
||||
+52
-8
@@ -231,8 +231,8 @@ static ssize_t pv__transfer_write_repeated(int fd, void *buf, size_t count, bool
|
||||
if ((EINTR == errno) || (EAGAIN == errno)) {
|
||||
/*
|
||||
* Interrupted by a signal - probably our
|
||||
* alarm() - so just return what we've
|
||||
* written so far.
|
||||
* alarm or interval timer - so just return
|
||||
* what we've written so far.
|
||||
*/
|
||||
return total_written;
|
||||
} else {
|
||||
@@ -267,8 +267,9 @@ static ssize_t pv__transfer_write_repeated(int fd, void *buf, size_t count, bool
|
||||
/*
|
||||
* Running the select() here seems to make PV eat a lot of
|
||||
* CPU in some cases, so instead we just go round the loop
|
||||
* again and rely on our alarm() to interrupt us if we run
|
||||
* out of time - also on our elapsed time check.
|
||||
* again and rely on our alarm or interval timer to
|
||||
* interrupt us if we run out of time - also on our elapsed
|
||||
* time check.
|
||||
*/
|
||||
if (count > 0) {
|
||||
debug("%s %d: %s (%ld %s, %ld %s)", "fd", fd,
|
||||
@@ -643,17 +644,60 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
|
||||
if (state->control.discard_input) {
|
||||
nwritten = state->transfer.to_write;
|
||||
} else if (state->transfer.to_write > 0) {
|
||||
if (signal(SIGALRM, SIG_IGN) == SIG_ERR) {
|
||||
pv_error(state, "%s: %s", _("failed to set alarm signal handler"), strerror(errno));
|
||||
} else {
|
||||
(void) alarm(1);
|
||||
|
||||
/*
|
||||
* Set an interval timer or an alarm to interrupt the write
|
||||
* with a signal if the write takes too long, so we can
|
||||
* continue producing progress information.
|
||||
*/
|
||||
#if HAVE_SETITIMER
|
||||
struct itimerval new_timer;
|
||||
|
||||
/*@-unrecog@ */
|
||||
/* splint doesn't know setitimer or ITIMER_REAL */
|
||||
memset(&new_timer, 0, sizeof(new_timer));
|
||||
new_timer.it_value.tv_sec = (time_t) (state->control.interval);
|
||||
new_timer.it_value.tv_usec = (suseconds_t) (((long) (state->control.interval * 1000000.0)) % 1000000);
|
||||
|
||||
/*
|
||||
* We have to set the interval so that the timer continues
|
||||
* to repeat while writes are attempted, especially as it's
|
||||
* possible that the initial timer run will expire
|
||||
* immediately if the period is less than 1 second.
|
||||
*/
|
||||
|
||||
new_timer.it_interval.tv_sec = new_timer.it_value.tv_sec;
|
||||
new_timer.it_interval.tv_usec = new_timer.it_value.tv_usec;
|
||||
|
||||
debug("%s: [%lds,%ldus]", "setting interval timer", (long) (new_timer.it_value.tv_sec),
|
||||
(long) (new_timer.it_value.tv_usec));
|
||||
|
||||
if (0 != setitimer(ITIMER_REAL, &new_timer, NULL)) {
|
||||
pv_error(state, "%s: %s", _("failed to set interval timer"), strerror(errno));
|
||||
}
|
||||
|
||||
#else /* ! HAVE_SETITIMER */
|
||||
(void) alarm(1);
|
||||
#endif /* HAVE_SETITIMER */
|
||||
nwritten = pv__transfer_write_repeated(STDOUT_FILENO,
|
||||
state->transfer.transfer_buffer +
|
||||
state->transfer.write_position,
|
||||
(size_t) (state->transfer.to_write),
|
||||
state->control.sync_after_write);
|
||||
#if HAVE_SETITIMER
|
||||
memset(&new_timer, 0, sizeof(new_timer));
|
||||
new_timer.it_interval.tv_sec = 0;
|
||||
new_timer.it_interval.tv_usec = 0;
|
||||
new_timer.it_value.tv_sec = 0;
|
||||
new_timer.it_value.tv_usec = 0;
|
||||
if (0 != setitimer(ITIMER_REAL, &new_timer, NULL)) {
|
||||
pv_error(state, "%s: %s", _("failed to clear interval timer"), strerror(errno));
|
||||
}
|
||||
|
||||
/*@+unrecog@ */
|
||||
#else /* ! HAVE_SETITIMER */
|
||||
(void) alarm(0);
|
||||
#endif /* HAVE_SETITIMER */
|
||||
}
|
||||
|
||||
if (0 == nwritten) {
|
||||
|
||||
Reference in New Issue
Block a user