Replace all gettimeofday() calls, and elapsed time arithmetic, with a new set of elapsed time functions based on clock_gettime(), to avoid faults due to time zone / DST changes and machine suspend/resume.

This commit is contained in:
Andrew Wood
2023-08-30 22:23:43 +01:00
parent 46e5948425
commit 1f320dd7a2
11 changed files with 433 additions and 219 deletions
+1
View File
@@ -19,6 +19,7 @@ src/main/remote.c \
src/main/version.c \
src/pv/cursor.c \
src/pv/display.c \
src/pv/elapsedtime.c \
src/pv/file.c \
src/pv/loop.c \
src/pv/number.c \
+2 -1
View File
@@ -84,6 +84,7 @@ is acknowledged and greatly appreciated:
* [Nick Black](https://nick-black.com) - added "`--bits`" option
* [Andrew Schulman](https://github.com/andrew-schulman) - provided reproducible example of terminal size detection issue in 1.7.17/1.7.18
* [fuschia74](https://github.com/fuchsia74) - provided "`--enable-static`" patch for "`configure`"
* [Wilhelm von Thiele](https://github.com/TurtleWilly) - assisted with OS X cleanups ([Issue#73](https://codeberg.org/a-j-wood/pv/issues/73), [Issue#74](https://codeberg.org/a-j-wood/pv/issues/74))
* [Wilhelm von Thiele](https://github.com/TurtleWilly) - assisted with OS X cleanups ([#73](https://codeberg.org/a-j-wood/pv/issues/73), [#74](https://codeberg.org/a-j-wood/pv/issues/74))
* Matějů Miroslav, Ing. - suggested fix for ETA and elapsed time faults when suspending and resuming a machine ([#13](https://codeberg.org/a-j-wood/pv/issues/13))
---
+1
View File
@@ -7,6 +7,7 @@
* security: keep self-contained copies of name and format string in PV internal state for memory safety
* fix: only report errors about missing files when starting to transfer from them, not while calculating size, and behave more like `cat`(1) by skipping them and moving on
* fix: auto-calculate total line count with "`--line-mode`" when all inputs are regular files
* fix: use `clock_gettime()` in ETA calculation to cope with machine suspend/resume ([#13](https://codeberg.org/a-j-wood/pv/issues/13))
* fix: if "`--width`" or "`--height`" were provided, do not change them when the window size changes ([#36](https://codeberg.org/a-j-wood/pv/issues/36))
* cleanup: switched the build system to GNU Automake
* cleanup: added a test for terminal width detection to "`make check`"
-1
View File
@@ -4,7 +4,6 @@ Bugs
----
* ([#5](https://codeberg.org/a-j-wood/pv/issues/5)) Transfer IPC leadership on exit of leader
* ([#13](https://codeberg.org/a-j-wood/pv/issues/13)) Use `clock_gettime()` in ETA calculation to cope with machine suspend/resume (Mateju Miroslav)
* ([#20](https://codeberg.org/a-j-wood/pv/issues/20)) Terminal state is not restored correctly in all cases (VA)
* ([#24](https://codeberg.org/a-j-wood/pv/issues/24)) Debian #890901 - Race condition with multiple "`pv -c`" leaves terminal state inconsistent (Lars Ellenberg, Viktor Ashirov)
* ([#34](https://codeberg.org/a-j-wood/pv/issues/34)) Continue timer even if input or output is blocking (Martin Probst - Jun 2017)
+13 -12
View File
@@ -11,6 +11,7 @@
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
@@ -30,15 +31,15 @@ extern "C" {
#define PV_DISPLAY_OUTPUTBUF 256
#define PV_DISPLAY_FINETA 512
#define RATE_GRANULARITY 100000 /* usec between -L rate chunks */
#define RATE_BURST_WINDOW 5 /* rate burst window (multiples of rate) */
#define REMOTE_INTERVAL 100000 /* usec between checks for -R */
#define BUFFER_SIZE 409600 /* default transfer buffer size */
#define BUFFER_SIZE_MAX 524288 /* max auto transfer buffer size */
#define MAX_READ_AT_ONCE 524288 /* max to read() in one go */
#define MAX_WRITE_AT_ONCE 524288 /* max to write() in one go */
#define TRANSFER_READ_TIMEOUT 90000 /* usec to time reads out at */
#define TRANSFER_WRITE_TIMEOUT 900000 /* usec to time writes out at */
#define RATE_GRANULARITY 100000000.0L /* nsec between -L rate chunks */
#define RATE_BURST_WINDOW 5 /* rate burst window (multiples of rate) */
#define REMOTE_INTERVAL 100000000 /* nsec between checks for -R */
#define BUFFER_SIZE 409600 /* default transfer buffer size */
#define BUFFER_SIZE_MAX 524288 /* max auto transfer buffer size */
#define MAX_READ_AT_ONCE 524288 /* max to read() in one go */
#define MAX_WRITE_AT_ONCE 524288 /* max to write() in one go */
#define TRANSFER_READ_TIMEOUT 0.09L /* seconds to time reads out at */
#define TRANSFER_WRITE_TIMEOUT 0.9L /* seconds to time writes out at */
#define MAXIMISE_BUFFER_FILL 1
@@ -125,8 +126,8 @@ struct pvstate_s {
* Signal handling *
*******************/
int pv_sig_old_stderr; /* see pv_sig_ttou() */
struct timeval pv_sig_tstp_time; /* see pv_sig_tstp() / __cont() */
struct timeval pv_sig_toffset; /* total time spent stopped */
struct timespec pv_sig_tstp_time; /* see pv_sig_tstp() / __cont() */
struct timespec pv_sig_toffset; /* total time spent stopped */
volatile sig_atomic_t pv_sig_newsize; /* whether we need to get term size again */
volatile sig_atomic_t pv_sig_abort; /* whether we need to abort right now */
volatile sig_atomic_t reparse_display; /* whether to re-check format string */
@@ -265,7 +266,7 @@ struct pvwatchfd_s {
struct stat sb_fd_link; /* lstat of fd symlink */
unsigned long long size; /* size of whole file, 0 if unknown */
long long position; /* position last seen at */
struct timeval start_time; /* time we started watching the fd */
struct timespec start_time; /* time we started watching the fd */
};
typedef struct pvwatchfd_s *pvwatchfd_t;
+38
View File
@@ -15,6 +15,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
@@ -76,6 +77,43 @@ extern int pv_snprintf(char *, size_t, const char *, ...);
* termination with '\0'.
*/
extern size_t pv_strlcat(char *, const char *, size_t);
/*
* Functions relating to elapsed time.
*/
/*
* Read the current elapsed time, relative to an unspecified point in the
* past, and store it in the given timespec buffer. The time is guaranteed
* to not go backwards and does not count time when the system was
* suspended. See clock_gettime(2) with CLOCK_MONOTONIC.
*/
void pv_elapsedtime_read(struct timespec *);
/* Set the time in the given timespec to zero. */
void pv_elapsedtime_zero(struct timespec *);
/* Copy the second timespec into the first. Analogous to strcpy(3). */
void pv_elapsedtime_copy(struct timespec *, const struct timespec *);
/*
* Return -1, 0, or 1 depending on whether the first time is earlier than,
* equal to, or later than the second time. Analogous to strcmp(3).
*/
int pv_elapsedtime_compare(const struct timespec *, const struct timespec *);
/* Add the latter two timespecs and store them in the first timespec. */
void pv_elapsedtime_add(struct timespec *, const struct timespec *, const struct timespec *);
/* Add a number of nanoseconds to the given timespec. */
void pv_elapsedtime_add_nsec(struct timespec *, long long);
/* Set the first timespec to the second minus the third. */
void pv_elapsedtime_subtract(struct timespec *, const struct timespec *, const struct timespec *);
/* Convert a timespec to seconds. */
long double pv_elapsedtime_seconds(const struct timespec *);
/*
* Main PV functions.
+198
View File
@@ -0,0 +1,198 @@
/*
* Functions relating to elapsed time.
*
* Copyright 2023 Andrew Wood
*
* Distributed under the Artistic License v2.0; see `doc/COPYING'.
*/
#include "config.h"
#include "pv.h"
#include "pv-internal.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
/*
* Read the current elapsed time, relative to an unspecified point in the
* past, and store it in the given timespec buffer. The time is guaranteed
* to not go backwards and does not count time when the system was
* suspended. See clock_gettime(2) with CLOCK_MONOTONIC.
*
* The read should not fail; if it does, the program is aborted with exit
* status 16.
*/
void pv_elapsedtime_read(struct timespec *return_time)
{
if (0 != clock_gettime(CLOCK_MONOTONIC, return_time)) {
fprintf(stderr, "%s: %s: %s\n", PACKAGE_NAME, "clock_gettime", strerror(errno));
exit(16);
}
}
/*
* Set the time in the given timespec to zero.
*/
void pv_elapsedtime_zero(struct timespec *zero_time)
{
if (NULL == zero_time)
return;
zero_time->tv_sec = 0;
zero_time->tv_nsec = 0;
}
/*
* Copy source_time into dest_time. Analogous to strcpy(3).
*/
void pv_elapsedtime_copy(struct timespec *dest_time, const struct timespec *source_time)
{
if (NULL == dest_time)
return;
if (NULL == source_time)
return;
dest_time->tv_sec = source_time->tv_sec;
dest_time->tv_nsec = source_time->tv_nsec;
}
/*
* Return -1, 0, or 1 depending on whether the first time is earlier than,
* equal to, or later than the second time. Analogous to strcmp(3).
*/
int pv_elapsedtime_compare(const struct timespec *first_time, const struct timespec *second_time)
{
/* Treat NULL as a zero time. */
if ((NULL == first_time) && (NULL == second_time))
return 0;
if ((NULL == first_time) && (NULL != second_time))
return -1;
if ((NULL != first_time) && (NULL == second_time))
return 1;
/* Check the seconds part, first */
if (first_time->tv_sec < second_time->tv_sec)
return -1;
if (first_time->tv_sec > second_time->tv_sec)
return 1;
/* Seconds are equal - compare nanoseconds. */
if (first_time->tv_nsec < second_time->tv_nsec)
return -1;
if (first_time->tv_nsec > second_time->tv_nsec)
return 1;
/* Nanoseconds are also equal - times are equal. */
return 0;
}
/*
* Add first_time and second_time, writing the result to return_time.
*/
void pv_elapsedtime_add(struct timespec *return_time, const struct timespec *first_time,
const struct timespec *second_time)
{
long long seconds, nanoseconds;
if (NULL == return_time)
return;
seconds = 0;
nanoseconds = 0;
if (NULL != first_time) {
seconds += first_time->tv_sec;
nanoseconds += first_time->tv_nsec;
}
if (NULL != second_time) {
seconds += second_time->tv_sec;
nanoseconds += second_time->tv_nsec;
}
seconds += nanoseconds / 1000000000;
nanoseconds = nanoseconds % 1000000000;
return_time->tv_sec = seconds;
return_time->tv_nsec = nanoseconds;
}
/*
* Add a number of nanoseconds to the given timespec.
*/
void pv_elapsedtime_add_nsec(struct timespec *return_time, long long add_nanoseconds)
{
long long seconds, nanoseconds;
if (NULL == return_time)
return;
seconds = return_time->tv_sec;
nanoseconds = return_time->tv_nsec + add_nanoseconds;
seconds += nanoseconds / 1000000000;
nanoseconds = nanoseconds % 1000000000;
return_time->tv_sec = seconds;
return_time->tv_nsec = nanoseconds;
}
/*
* Set the return timespec to the first time minus the second time.
*/
void pv_elapsedtime_subtract(struct timespec *return_time, const struct timespec *first_time,
const struct timespec *second_time)
{
long long seconds, nanoseconds;
if (NULL == return_time)
return;
seconds = 0;
nanoseconds = 0;
if (NULL != first_time) {
seconds += first_time->tv_sec;
nanoseconds += first_time->tv_nsec;
}
if (NULL != second_time) {
seconds -= second_time->tv_sec;
nanoseconds -= second_time->tv_nsec;
}
seconds += nanoseconds / 1000000000;
nanoseconds = nanoseconds % 1000000000;
if (nanoseconds < 0) {
seconds--;
nanoseconds = 1000000000 + nanoseconds;
}
return_time->tv_sec = seconds;
return_time->tv_nsec = nanoseconds;
}
/*
* Convert a timespec to seconds.
*/
long double pv_elapsedtime_seconds(const struct timespec *elapsed_time)
{
long double seconds;
if (NULL == elapsed_time)
return 0.0;
seconds = (long double) elapsed_time->tv_sec;
seconds += (long double) (elapsed_time->tv_nsec) / 1000000000.0L;
return seconds;
}
/* EOF */
+140 -163
View File
@@ -26,24 +26,6 @@
#include <sys/stat.h>
/*
* Add the given number of microseconds (which may be negative) to the given
* timeval.
*/
static void pv_timeval_add_usec(struct timeval *val, long usec)
{
val->tv_usec += usec;
while (val->tv_usec < 0) {
val->tv_sec--;
val->tv_usec += 1000000;
}
while (val->tv_usec >= 1000000) {
val->tv_sec++;
val->tv_usec -= 1000000;
}
}
/*
* Pipe data from a list of files to standard output, giving information
* about the transfer on standard error according to the given options.
@@ -53,12 +35,12 @@ static void pv_timeval_add_usec(struct timeval *val, long usec)
int pv_main_loop(pvstate_t state)
{
long written, lineswritten;
long long total_written, since_last, cansend;
long long total_written, transferred_since_last, cansend;
long double target;
int eof_in, eof_out, final_update;
struct timeval start_time, next_update, next_ratecheck, cur_time;
struct timeval init_time, next_remotecheck;
long double elapsed;
struct timespec start_time, next_update, next_ratecheck, cur_time;
struct timespec init_time, next_remotecheck, transfer_elapsed;
long double elapsed_seconds;
struct stat sb;
int fd, file_idx;
@@ -71,8 +53,9 @@ int pv_main_loop(pvstate_t state)
* "total_written" is the total bytes written since the start,
* or in line mode, the total lines written since the start.
*
* "since_last" is the bytes written since the last display,
* or in line mode, the lines written since the last display.
* "transferred_since_last" is the bytes written since the last
* display, or in line mode, the lines written since the last
* display.
*
* The remaining variables are all unchanged by linemode.
*/
@@ -84,26 +67,22 @@ int pv_main_loop(pvstate_t state)
eof_in = 0;
eof_out = 0;
total_written = 0;
since_last = 0;
transferred_since_last = 0;
state->initial_offset = 0;
gettimeofday(&start_time, NULL);
gettimeofday(&cur_time, NULL);
pv_elapsedtime_read(&cur_time);
pv_elapsedtime_copy(&start_time, &cur_time);
next_update.tv_sec = start_time.tv_sec;
next_update.tv_usec = start_time.tv_usec;
pv_elapsedtime_copy(&next_ratecheck, &cur_time);
pv_elapsedtime_copy(&next_remotecheck, &cur_time);
pv_elapsedtime_copy(&next_update, &cur_time);
if ((state->delay_start > 0)
&& (state->delay_start > state->interval)) {
pv_timeval_add_usec(&next_update, (long) (1000000.0 * state->delay_start));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->delay_start));
} else {
pv_timeval_add_usec(&next_update, (long) (1000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
}
next_ratecheck.tv_sec = start_time.tv_sec;
next_ratecheck.tv_usec = start_time.tv_usec;
next_remotecheck.tv_sec = start_time.tv_sec;
next_remotecheck.tv_usec = start_time.tv_usec;
target = 0;
final_update = 0;
file_idx = 0;
@@ -159,29 +138,26 @@ int pv_main_loop(pvstate_t state)
cansend = 0;
/*
* Check for remote messages from -R every short while
* Check for remote messages from -R every short while.
*/
if ((cur_time.tv_sec > next_remotecheck.tv_sec)
|| (cur_time.tv_sec == next_remotecheck.tv_sec && cur_time.tv_usec >= next_remotecheck.tv_usec)) {
if (pv_elapsedtime_compare(&cur_time, &next_remotecheck) > 0) {
pv_remote_check(state);
pv_timeval_add_usec(&next_remotecheck, REMOTE_INTERVAL);
pv_elapsedtime_add_nsec(&next_remotecheck, REMOTE_INTERVAL);
}
if (state->pv_sig_abort)
break;
if (state->rate_limit > 0) {
gettimeofday(&cur_time, NULL);
if ((cur_time.tv_sec > next_ratecheck.tv_sec)
|| (cur_time.tv_sec == next_ratecheck.tv_sec && cur_time.tv_usec >= next_ratecheck.tv_usec)) {
pv_elapsedtime_read(&cur_time);
if (pv_elapsedtime_compare(&cur_time, &next_ratecheck) > 0) {
target +=
((long double) (state->rate_limit)) / (long double) (1000000 / RATE_GRANULARITY);
long double burstMax = ((long double) (state->rate_limit * RATE_BURST_WINDOW));
if (target > burstMax) {
target = burstMax;
((long double) (state->rate_limit)) / (long double) (1000000000.0 / RATE_GRANULARITY);
long double burst_max = ((long double) (state->rate_limit * RATE_BURST_WINDOW));
if (target > burst_max) {
target = burst_max;
}
pv_timeval_add_usec(&next_ratecheck, RATE_GRANULARITY);
pv_elapsedtime_add_nsec(&next_ratecheck, RATE_GRANULARITY);
}
cansend = target;
}
@@ -216,12 +192,12 @@ int pv_main_loop(pvstate_t state)
}
if (state->linemode) {
since_last += lineswritten;
transferred_since_last += lineswritten;
total_written += lineswritten;
if (state->rate_limit > 0)
target -= lineswritten;
} else {
since_last += written;
transferred_since_last += written;
total_written += written;
if (state->rate_limit > 0)
target -= written;
@@ -239,15 +215,19 @@ int pv_main_loop(pvstate_t state)
}
}
gettimeofday(&cur_time, NULL);
/* Now check the current time. */
pv_elapsedtime_read(&cur_time);
/* If full EOF, final update, and force a display updaate. */
if (eof_in && eof_out) {
final_update = 1;
if ((state->display_visible)
|| (0 == state->delay_start))
next_update.tv_sec = cur_time.tv_sec - 1;
|| (0 == state->delay_start)) {
pv_elapsedtime_copy(&next_update, &cur_time);
}
}
/* Just go round the loop again if there's no display. */
if (state->no_display)
continue;
@@ -258,6 +238,7 @@ int pv_main_loop(pvstate_t state)
* was received.
*/
if (state->wait) {
/* Restart the loop if nothing written yet. */
if (state->linemode) {
if (lineswritten < 1)
continue;
@@ -279,47 +260,47 @@ int pv_main_loop(pvstate_t state)
* SIGTSTOP so things don't mess up.
*/
pv_sig_nopause();
gettimeofday(&start_time, NULL);
state->pv_sig_toffset.tv_sec = 0;
state->pv_sig_toffset.tv_usec = 0;
pv_elapsedtime_read(&start_time);
pv_elapsedtime_zero(&(state->pv_sig_toffset));
pv_sig_allowpause();
next_update.tv_sec = start_time.tv_sec;
next_update.tv_usec = start_time.tv_usec;
pv_timeval_add_usec(&next_update, (long) (1000000.0 * state->interval));
/*
* Start the display, but only at the next interval,
* not immediately.
*/
pv_elapsedtime_copy(&next_update, &start_time);
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
}
if ((cur_time.tv_sec < next_update.tv_sec)
|| (cur_time.tv_sec == next_update.tv_sec && cur_time.tv_usec < next_update.tv_usec)) {
/* Restart the loop if it's not time to update the display. */
if (pv_elapsedtime_compare(&cur_time, &next_update) < 0) {
continue;
}
pv_timeval_add_usec(&next_update, (long) (1000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
if (next_update.tv_sec < cur_time.tv_sec) {
next_update.tv_sec = cur_time.tv_sec;
next_update.tv_usec = cur_time.tv_usec;
} else if (next_update.tv_sec == cur_time.tv_sec && next_update.tv_usec < cur_time.tv_usec) {
next_update.tv_usec = cur_time.tv_usec;
}
/* Set the "next update" time to now, if it's in the past. */
if (pv_elapsedtime_compare(&next_update, &cur_time) < 0)
pv_elapsedtime_copy(&next_update, &cur_time);
init_time.tv_sec = start_time.tv_sec + state->pv_sig_toffset.tv_sec;
init_time.tv_usec = start_time.tv_usec + state->pv_sig_toffset.tv_usec;
if (init_time.tv_usec >= 1000000) {
init_time.tv_sec++;
init_time.tv_usec -= 1000000;
}
if (init_time.tv_usec < 0) {
init_time.tv_sec--;
init_time.tv_usec += 1000000;
}
/*
* Calculate the effective start time: the time we actually
* started, plus the total time we spent stopped.
*/
pv_elapsedtime_add(&init_time, &start_time, &(state->pv_sig_toffset));
elapsed = cur_time.tv_sec - init_time.tv_sec;
elapsed += (cur_time.tv_usec - init_time.tv_usec) / 1000000.0;
/*
* Now get the effective elapsed transfer time - current
* time minus effective start time.
*/
pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &init_time);
elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
if (final_update)
since_last = -1;
transferred_since_last = -1;
/* Resize the display, if a resize signal was received. */
if (state->pv_sig_newsize) {
unsigned int new_width, new_height;
@@ -335,9 +316,9 @@ int pv_main_loop(pvstate_t state)
state->height = new_height;
}
pv_display(state, elapsed, since_last, total_written);
pv_display(state, elapsed_seconds, transferred_since_last, total_written);
since_last = 0;
transferred_since_last = 0;
}
if (state->cursor) {
@@ -368,10 +349,10 @@ int pv_main_loop(pvstate_t state)
int pv_watchfd_loop(pvstate_t state)
{
struct pvwatchfd_s info;
long long position_now, total_written, since_last;
struct timeval next_update, cur_time;
struct timeval init_time, next_remotecheck;
long double elapsed;
long long position_now, total_written, transferred_since_last;
struct timespec next_update, cur_time;
struct timespec init_time, next_remotecheck, transfer_elapsed;
long double elapsed_seconds;
int ended;
int first_check;
int rc;
@@ -401,29 +382,24 @@ int pv_watchfd_loop(pvstate_t state)
}
}
gettimeofday(&(info.start_time), NULL);
gettimeofday(&cur_time, NULL);
next_update.tv_sec = info.start_time.tv_sec;
next_update.tv_usec = info.start_time.tv_usec;
pv_timeval_add_usec(&next_update, (long) (1000000.0 * state->interval));
next_remotecheck.tv_sec = info.start_time.tv_sec;
next_remotecheck.tv_usec = info.start_time.tv_usec;
pv_elapsedtime_read(&cur_time);
pv_elapsedtime_copy(&(info.start_time), &cur_time);
pv_elapsedtime_copy(&next_remotecheck, &cur_time);
pv_elapsedtime_copy(&next_update, &cur_time);
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
ended = 0;
total_written = 0;
since_last = 0;
transferred_since_last = 0;
first_check = 1;
while (!ended) {
/*
* Check for remote messages from -R every short while
* Check for remote messages from -R every short while.
*/
if ((cur_time.tv_sec > next_remotecheck.tv_sec)
|| (cur_time.tv_sec == next_remotecheck.tv_sec && cur_time.tv_usec >= next_remotecheck.tv_usec)) {
if (pv_elapsedtime_compare(&cur_time, &next_remotecheck) > 0) {
pv_remote_check(state);
pv_timeval_add_usec(&next_remotecheck, REMOTE_INTERVAL);
pv_elapsedtime_add_nsec(&next_remotecheck, REMOTE_INTERVAL);
}
if (state->pv_sig_abort)
@@ -434,7 +410,7 @@ int pv_watchfd_loop(pvstate_t state)
if (position_now < 0) {
ended = 1;
} else {
since_last += position_now - total_written;
transferred_since_last += position_now - total_written;
total_written = position_now;
if (first_check) {
state->initial_offset = position_now;
@@ -442,15 +418,19 @@ int pv_watchfd_loop(pvstate_t state)
}
}
gettimeofday(&cur_time, NULL);
pv_elapsedtime_read(&cur_time);
/* Ended - force a display update. */
if (ended) {
ended = 1;
next_update.tv_sec = cur_time.tv_sec - 1;
pv_elapsedtime_copy(&next_update, &cur_time);
}
if ((cur_time.tv_sec < next_update.tv_sec)
|| (cur_time.tv_sec == next_update.tv_sec && cur_time.tv_usec < next_update.tv_usec)) {
/*
* Restart the loop after a brief delay, if it's not time to
* update the display.
*/
if (pv_elapsedtime_compare(&cur_time, &next_update) < 0) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000;
@@ -458,32 +438,30 @@ int pv_watchfd_loop(pvstate_t state)
continue;
}
pv_timeval_add_usec(&next_update, (long) (1000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
if (next_update.tv_sec < cur_time.tv_sec) {
next_update.tv_sec = cur_time.tv_sec;
next_update.tv_usec = cur_time.tv_usec;
} else if (next_update.tv_sec == cur_time.tv_sec && next_update.tv_usec < cur_time.tv_usec) {
next_update.tv_usec = cur_time.tv_usec;
}
/* Set the "next update" time to now, if it's in the past. */
if (pv_elapsedtime_compare(&next_update, &cur_time) < 0)
pv_elapsedtime_copy(&next_update, &cur_time);
init_time.tv_sec = info.start_time.tv_sec + state->pv_sig_toffset.tv_sec;
init_time.tv_usec = info.start_time.tv_usec + state->pv_sig_toffset.tv_usec;
if (init_time.tv_usec >= 1000000) {
init_time.tv_sec++;
init_time.tv_usec -= 1000000;
}
if (init_time.tv_usec < 0) {
init_time.tv_sec--;
init_time.tv_usec += 1000000;
}
/*
* Calculate the effective start time: the time we actually
* started, plus the total time we spent stopped.
*/
pv_elapsedtime_add(&init_time, &(info.start_time), &(state->pv_sig_toffset));
elapsed = cur_time.tv_sec - init_time.tv_sec;
elapsed += (cur_time.tv_usec - init_time.tv_usec) / 1000000.0;
/*
* Now get the effective elapsed transfer time - current
* time minus effective start time.
*/
pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &init_time);
elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
if (ended)
since_last = -1;
transferred_since_last = -1;
/* Resize the display, if a resize signal was received. */
if (state->pv_sig_newsize) {
unsigned int new_width, new_height;
@@ -499,9 +477,9 @@ int pv_watchfd_loop(pvstate_t state)
state->height = new_height;
}
pv_display(state, elapsed, since_last, total_written);
pv_display(state, elapsed_seconds, transferred_since_last, total_written);
since_last = 0;
transferred_since_last = 0;
}
if (!state->numeric)
@@ -530,7 +508,7 @@ int pv_watchpid_loop(pvstate_t state)
struct pvstate_s *state_array = NULL;
int array_length = 0;
int fd_to_idx[FD_SETSIZE] = { 0, };
struct timeval next_update, cur_time;
struct timespec next_update, cur_time;
int idx;
int prev_displayed_lines, blank_lines;
int first_pass = 1;
@@ -570,11 +548,9 @@ int pv_watchpid_loop(pvstate_t state)
* Get things ready for the main loop.
*/
gettimeofday(&cur_time, NULL);
next_update.tv_sec = cur_time.tv_sec;
next_update.tv_usec = cur_time.tv_usec;
pv_timeval_add_usec(&next_update, (long) (1000000.0 * state->interval));
pv_elapsedtime_read(&cur_time);
pv_elapsedtime_copy(&next_update, &cur_time);
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
for (idx = 0; idx < FD_SETSIZE; idx++) {
fd_to_idx[idx] = -1;
@@ -588,7 +564,7 @@ int pv_watchpid_loop(pvstate_t state)
if (state->pv_sig_abort)
break;
gettimeofday(&cur_time, NULL);
pv_elapsedtime_read(&cur_time);
if (kill(state->watch_pid, 0) != 0) {
if (first_pass) {
@@ -603,8 +579,11 @@ int pv_watchpid_loop(pvstate_t state)
break;
}
if ((cur_time.tv_sec < next_update.tv_sec)
|| (cur_time.tv_sec == next_update.tv_sec && cur_time.tv_usec < next_update.tv_usec)) {
/*
* Restart the loop after a brief delay, if it's not time to
* update the display.
*/
if (pv_elapsedtime_compare(&cur_time, &next_update) < 0) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000;
@@ -612,15 +591,13 @@ int pv_watchpid_loop(pvstate_t state)
continue;
}
pv_timeval_add_usec(&next_update, (long) (1000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
if (next_update.tv_sec < cur_time.tv_sec) {
next_update.tv_sec = cur_time.tv_sec;
next_update.tv_usec = cur_time.tv_usec;
} else if (next_update.tv_sec == cur_time.tv_sec && next_update.tv_usec < cur_time.tv_usec) {
next_update.tv_usec = cur_time.tv_usec;
}
/* Set the "next update" time to now, if it's in the past. */
if (pv_elapsedtime_compare(&next_update, &cur_time) < 0)
pv_elapsedtime_copy(&next_update, &cur_time);
/* Resize the display, if a resize signal was received. */
if (state->pv_sig_newsize) {
state->pv_sig_newsize = 0;
pv_screensize(&(state->width), &(state->height));
@@ -651,9 +628,9 @@ int pv_watchpid_loop(pvstate_t state)
displayed_lines = 0;
for (fd = 0; fd < FD_SETSIZE; fd++) {
long long position_now, since_last;
struct timeval init_time;
long double elapsed;
long long position_now, transferred_since_last;
struct timespec init_time, transfer_elapsed;
long double elapsed_seconds;
if (displayed_lines >= (int) (state->height))
break;
@@ -689,31 +666,31 @@ int pv_watchpid_loop(pvstate_t state)
continue;
}
since_last = position_now - info_array[idx].position;
transferred_since_last = position_now - info_array[idx].position;
info_array[idx].position = position_now;
init_time.tv_sec = info_array[idx].start_time.tv_sec + state->pv_sig_toffset.tv_sec;
init_time.tv_usec = info_array[idx].start_time.tv_usec + state->pv_sig_toffset.tv_usec;
if (init_time.tv_usec >= 1000000) {
init_time.tv_sec++;
init_time.tv_usec -= 1000000;
}
if (init_time.tv_usec < 0) {
init_time.tv_sec--;
init_time.tv_usec += 1000000;
}
/*
* Calculate the effective start time: the time we actually
* started, plus the total time we spent stopped.
*/
pv_elapsedtime_add(&init_time, &(info_array[idx].start_time), &(state->pv_sig_toffset));
elapsed = cur_time.tv_sec - init_time.tv_sec;
elapsed += (cur_time.tv_usec - init_time.tv_usec) / 1000000.0;
/*
* Now get the effective elapsed transfer time - current
* time minus effective start time.
*/
pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &init_time);
elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
if (displayed_lines > 0) {
debug("%s", "adding newline");
pv_write_retry(STDERR_FILENO, "\n", 1);
}
debug("%s %d [%d]: %Lf / %Ld / %Ld", "fd", fd, idx, elapsed, since_last, position_now);
debug("%s %d [%d]: %Lf / %Ld / %Ld", "fd", fd, idx, elapsed_seconds, transferred_since_last, position_now);
pv_display(&(state_array[idx]), elapsed, since_last, position_now);
pv_display(&(state_array[idx]), elapsed_seconds, transferred_since_last, position_now);
displayed_lines++;
}
+14 -20
View File
@@ -12,7 +12,6 @@
#include <signal.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -55,7 +54,7 @@ static void pv_sig_ttou( __attribute__((unused))
static void pv_sig_tstp( __attribute__((unused))
int s)
{
gettimeofday(&(pv_sig_state->pv_sig_tstp_time), NULL);
pv_elapsedtime_read(&(pv_sig_state->pv_sig_tstp_time));
raise(SIGSTOP);
}
@@ -68,11 +67,13 @@ static void pv_sig_tstp( __attribute__((unused))
static void pv_sig_cont( __attribute__((unused))
int s)
{
struct timeval tv;
struct timespec current_time;
struct timespec time_spent_stopped;
struct termios t;
pv_sig_state->pv_sig_newsize = 1;
/* if this SIGCONT didn't follow a SIGTSTP so we have no stop time */
if (0 == pv_sig_state->pv_sig_tstp_time.tv_sec) {
tcgetattr(STDERR_FILENO, &t);
t.c_lflag |= TOSTOP;
@@ -83,21 +84,16 @@ static void pv_sig_cont( __attribute__((unused))
return;
}
gettimeofday(&tv, NULL);
pv_elapsedtime_read(&current_time);
pv_sig_state->pv_sig_toffset.tv_sec += (tv.tv_sec - pv_sig_state->pv_sig_tstp_time.tv_sec);
pv_sig_state->pv_sig_toffset.tv_usec += (tv.tv_usec - pv_sig_state->pv_sig_tstp_time.tv_usec);
if (pv_sig_state->pv_sig_toffset.tv_usec >= 1000000) {
pv_sig_state->pv_sig_toffset.tv_sec++;
pv_sig_state->pv_sig_toffset.tv_usec -= 1000000;
}
if (pv_sig_state->pv_sig_toffset.tv_usec < 0) {
pv_sig_state->pv_sig_toffset.tv_sec--;
pv_sig_state->pv_sig_toffset.tv_usec += 1000000;
}
/* time spent stopped = current time - time SIGTSTP received */
pv_elapsedtime_subtract(&time_spent_stopped, &current_time, &(pv_sig_state->pv_sig_tstp_time));
pv_sig_state->pv_sig_tstp_time.tv_sec = 0;
pv_sig_state->pv_sig_tstp_time.tv_usec = 0;
/* add time spent stopped the total stopped-time count */
pv_elapsedtime_add(&(pv_sig_state->pv_sig_toffset), &(pv_sig_state->pv_sig_toffset), &time_spent_stopped);
/* reset the SIGTSTP receipt time */
pv_elapsedtime_zero(&(pv_sig_state->pv_sig_tstp_time));
if (pv_sig_state->pv_sig_old_stderr != -1) {
dup2(pv_sig_state->pv_sig_old_stderr, STDERR_FILENO);
@@ -145,10 +141,8 @@ void pv_sig_init(pvstate_t state)
pv_sig_state = state;
pv_sig_state->pv_sig_old_stderr = -1;
pv_sig_state->pv_sig_tstp_time.tv_sec = 0;
pv_sig_state->pv_sig_tstp_time.tv_usec = 0;
pv_sig_state->pv_sig_toffset.tv_sec = 0;
pv_sig_state->pv_sig_toffset.tv_usec = 0;
pv_elapsedtime_zero(&(pv_sig_state->pv_sig_tstp_time));
pv_elapsedtime_zero(&(pv_sig_state->pv_sig_toffset));
/*
* Ignore SIGPIPE, so we don't die if stdout is a pipe and the other
+25 -21
View File
@@ -32,21 +32,21 @@
* buffer as full as we can.
*
* We stop retrying if the time elapsed since this function was entered
* reaches TRANSFER_READ_TIMEOUT microseconds.
* reaches TRANSFER_READ_TIMEOUT seconds.
*/
static ssize_t pv__transfer_read_repeated(int fd, void *buf, size_t count)
{
struct timeval start_time;
struct timespec start_time;
ssize_t total_read;
gettimeofday(&start_time, NULL);
pv_elapsedtime_read(&start_time);
total_read = 0;
while (count > 0) {
ssize_t nread;
struct timeval now;
long elapsed_usec;
struct timespec cur_time, transfer_elapsed;
long double elapsed_seconds;
nread = read(fd, buf, count > MAX_READ_AT_ONCE ? MAX_READ_AT_ONCE : count);
if (nread < 0)
@@ -59,11 +59,13 @@ static ssize_t pv__transfer_read_repeated(int fd, void *buf, size_t count)
if (0 == nread)
return total_read;
gettimeofday(&now, NULL);
elapsed_usec = 1000000 * (now.tv_sec - start_time.tv_sec) + (now.tv_usec - start_time.tv_usec);
if (elapsed_usec > TRANSFER_READ_TIMEOUT) {
debug("%s %d: %s (%ld %s)", "fd", fd,
"stopping read - timer expired", elapsed_usec, "usec elapsed");
pv_elapsedtime_read(&cur_time);
pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &start_time);
elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
if (elapsed_seconds > TRANSFER_READ_TIMEOUT) {
debug("%s %d: %s (%lf %s)", "fd", fd,
"stopping read - timer expired", elapsed_seconds, "sec elapsed");
return total_read;
}
@@ -100,21 +102,21 @@ static ssize_t pv__transfer_read_repeated(int fd, void *buf, size_t count)
* fsync() if _POSIX_SYNCHRONIZED_IO is not > 0).
*
* We stop retrying if the time elapsed since this function was entered
* reaches TRANSFER_WRITE_TIMEOUT microseconds.
* reaches TRANSFER_WRITE_TIMEOUT seconds.
*/
static ssize_t pv__transfer_write_repeated(int fd, void *buf, size_t count, bool sync_after_write)
{
struct timeval start_time;
struct timespec start_time;
ssize_t total_written;
gettimeofday(&start_time, NULL);
pv_elapsedtime_read(&start_time);
total_written = 0;
while (count > 0) {
ssize_t nwritten;
struct timeval now;
long elapsed_usec;
struct timespec cur_time, transfer_elapsed;
long double elapsed_seconds;
size_t asked_to_write;
asked_to_write = count > MAX_WRITE_AT_ONCE ? MAX_WRITE_AT_ONCE : count;
@@ -163,11 +165,13 @@ static ssize_t pv__transfer_write_repeated(int fd, void *buf, size_t count, bool
if (0 == nwritten)
return total_written;
gettimeofday(&now, NULL);
elapsed_usec = 1000000 * (now.tv_sec - start_time.tv_sec) + (now.tv_usec - start_time.tv_usec);
if (elapsed_usec > TRANSFER_WRITE_TIMEOUT) {
debug("%s %d: %s (%ld %s)", "fd", fd,
"stopping write - timer expired", elapsed_usec, "usec elapsed");
pv_elapsedtime_read(&cur_time);
pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &start_time);
elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
if (elapsed_seconds > TRANSFER_WRITE_TIMEOUT) {
debug("%s %d: %s (%lf %s)", "fd", fd,
"stopping write - timer expired", elapsed_seconds, "sec elapsed");
return total_written;
}
@@ -175,7 +179,7 @@ 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 gettimeofday() check.
* out of time - also on our elapsed time check.
*/
if (count > 0) {
#if 0 /* disabled after 1.6.0 - see comment above */
+1 -1
View File
@@ -440,7 +440,7 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
state_array[use_idx].reparse_display = 1;
gettimeofday(&(info_array[use_idx].start_time), NULL);
pv_elapsedtime_read(&(info_array[use_idx].start_time));
state_array[use_idx].initial_offset = 0;
info_array[use_idx].position = 0;