diff --git a/src/include/pv-internal.h b/src/include/pv-internal.h index 07f4518..b47b23b 100644 --- a/src/include/pv-internal.h +++ b/src/include/pv-internal.h @@ -520,18 +520,12 @@ struct pvwatchfd_s { off_t size; /* size of whole file, 0 if unknown */ off_t position; /* position last seen at */ struct timespec start_time; /* time we started watching the fd */ - struct timespec fd_last_seen; /* time the fd was last seen open */ - pid_t watch_pid; /* PID to watch */ + struct timespec end_time; /* time the fd was marked as closed */ + pid_t watch_pid; /* PID the fd belongs to */ int watch_fd; /* fd to watch */ + bool closed; /* true once the fd is closed */ bool displayable; /* false if not displayable */ bool unused; /* true if free for re-use */ - /* - * After the "watch_fd" is found to be closed, "fd_last_seen" stops - * being updated; when "fd_last_seen" is old enough, "unused" - * becomes true so the slot can be re-used. This allows fd - * information to be held on-screen for a short while after the fd - * is closed (#81). - */ }; typedef struct pvwatchfd_s *pvwatchfd_t; diff --git a/src/pv/loop.c b/src/pv/loop.c index 1b6c191..9950e20 100644 --- a/src/pv/loop.c +++ b/src/pv/loop.c @@ -712,7 +712,6 @@ int pv_watchfd_loop(pvstate_t state) int fd; /* watched fd, or -1 for all */ pvwatchfd_t info_array; /* watch information for each fd */ int array_length; /* length of watch info array */ - struct timespec pid_last_seen; /* when this PID was last seen */ bool finished; /* "PID:FD": fd closed; or PID gone */ } *watching; unsigned int watch_idx; @@ -722,24 +721,13 @@ int pv_watchfd_loop(pvstate_t state) /* * TODO: new logic: - * For each watching[] item, "pid_last_seen" stops being updated - * when the PID ceases to exist. If "fd" is >=0 then - * "pid_last_seen" is set to the newest of the "fd_last_seen" values - * of the info_array[] fd info items. When "pid_last_seen" is too - * old, "finished" becomes true so this watching[] item stops being - * displayed. - * * In each watching[].info_array[] fd info item, once its "watch_fd" - * is closed, its "fd_last_seen" stops being updated, and once - * that's stopped updating long enough, its "unused" becomes true. - * - * All of this allows information to be held on-screen for a short + * is closed, set its "closed" flag to true and its "end_time" to + * now but don't mark it "unused" until its "end_time" is long + * enough ago, so the information is held on-screen for a short * while after the fd closes or the PID exits (#81). */ - /* TODO: implement the above (pid_last_seen, fd_last_seen) */ - /* TODO: check whether pid_last_seen is needed, or only fd_last_seen */ - /* If there's nothing to watch, do nothing at all. */ if (state->watchfd.count < 1) return 0; @@ -953,10 +941,9 @@ int pv_watchfd_loop(pvstate_t state) if (rc != 0) { /* * PID now inaccessible - mark it as - * finished and skip over it. + * finished. */ watching[watch_idx].finished = true; - continue; } } else { /* @@ -979,7 +966,6 @@ int pv_watchfd_loop(pvstate_t state) NULL != watching[watch_idx].info_array && info_idx < watching[watch_idx].array_length; info_idx++) { off_t position_now; - struct timespec init_time, transfer_elapsed; pvwatchfd_t info_item = &(watching[watch_idx].info_array[info_idx]); /* Skip unused array entries. */ @@ -996,7 +982,8 @@ int pv_watchfd_loop(pvstate_t state) * changed. */ if (pv_watchfd_changed(info_item)) { - debug("%s %d: %s", "fd", info_item->watch_fd, "removing"); + debug("%s %d: %s", "fd", info_item->watch_fd, + "non-displayable, and has changed - removing"); info_item->unused = true; info_item->displayable = false; pv_freecontents_watchfd(info_item); @@ -1014,34 +1001,86 @@ int pv_watchfd_loop(pvstate_t state) * changed. */ - position_now = pv_watchfd_position(info_item); + position_now = -1; - if (position_now < 0) { - debug("%s %d: %s", "fd", info_item->watch_fd, "removing"); - info_item->unused = true; - info_item->displayable = false; - pv_freecontents_watchfd(info_item); - continue; + if (info_item->closed) { + /* + * Closed fd - check how long since + * it was closed. + */ + struct timespec time_since_closed; + long double seconds_since_closed; + + memset(&time_since_closed, 0, sizeof(time_since_closed)); + pv_elapsedtime_subtract(&time_since_closed, &cur_time, &(info_item->end_time)); + seconds_since_closed = pv_elapsedtime_seconds(&time_since_closed); + + /* Closed for long enough - remove. */ + /* TODO: configurable period */ + if (seconds_since_closed > 5.0) { + debug("%s %d: %s (%Lf s)", "fd", info_item->watch_fd, + "closed for long enough - removing", seconds_since_closed); + info_item->unused = true; + info_item->displayable = false; + pv_freecontents_watchfd(info_item); + continue; + } + + } else { + + /* + * Open fd - get its current + * position. + */ + + position_now = pv_watchfd_position(info_item); + + if (position_now < 0) { + /* + * The fd was closed - mark + * as closed. + */ + debug("%s %d: %s", "fd", info_item->watch_fd, "marking as closed"); + pv_elapsedtime_copy(&(info_item->end_time), &cur_time); + info_item->closed = true; + } } - info_item->position = position_now; + if (position_now >= 0) { + /* + * If the fd is still open and we + * got its current position, update + * its position and timers. + */ + struct timespec init_time, transfer_elapsed; - memset(&init_time, 0, sizeof(init_time)); - memset(&transfer_elapsed, 0, sizeof(transfer_elapsed)); + info_item->position = position_now; + + memset(&init_time, 0, sizeof(init_time)); + memset(&transfer_elapsed, 0, sizeof(transfer_elapsed)); + + /* + * Calculate the effective start + * time: the time we actually + * started, plus the total time we + * spent stopped. + */ + pv_elapsedtime_add(&init_time, &(info_item->start_time), + &(state->signal.toffset)); + + /* + * Now get the effective elapsed + * transfer time - current time + * minus effective start time. + */ + pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &init_time); + + info_item->transfer.elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed); + } /* - * Calculate the effective start time: the time we actually - * started, plus the total time we spent stopped. + * Now display the information about the fd. */ - pv_elapsedtime_add(&init_time, &(info_item->start_time), &(state->signal.toffset)); - - /* - * Now get the effective elapsed transfer time - current - * time minus effective start time. - */ - pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &init_time); - - info_item->transfer.elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed); if (displayed_lines > 0) { debug("%s", "adding newline"); @@ -1050,15 +1089,15 @@ int pv_watchfd_loop(pvstate_t state) debug("%s %d, %s %d [%d/%d]: %Lf / %Ld", "pid", (int) (info_item->watch_pid), "fd", info_item->watch_fd, watch_idx, info_idx, info_item->transfer.elapsed_seconds, - position_now); + info_item->position); if (terminal_resized) { pv_watchpid_setname(state, info_item); info_item->flags.reparse_display = 1; } - info_item->transfer.transferred = position_now; - info_item->transfer.total_written = position_now; + info_item->transfer.transferred = info_item->position; + info_item->transfer.total_written = info_item->position; state->control.name = info_item->display_name; state->control.size = info_item->size; @@ -1119,25 +1158,16 @@ int pv_watchfd_loop(pvstate_t state) } } - if (!state->control.numeric) - pv_tty_write(&(state->flags), "\n", 1); - - /* - * Clean up our displayed lines after the display loop. - */ - blank_lines = prev_displayed_lines; - while (blank_lines > 0) { - pvdisplay_width_t blank_count; - for (blank_count = 0; blank_count < state->control.width; blank_count++) - pv_tty_write(&(state->flags), " ", 1); - pv_tty_write(&(state->flags), "\r", 1); - blank_lines--; - if (blank_lines > 0) + if (!state->control.numeric) { + /* + * Move the cursor past the displayed lines, so the last + * output stays on the screen and isn't overwritten by the + * command prompt (#81). + */ + while (prev_displayed_lines > 0) { pv_tty_write(&(state->flags), "\n", 1); - } - while (prev_displayed_lines > 1) { - pv_tty_write(&(state->flags), "\033[A", 3); - prev_displayed_lines--; + prev_displayed_lines--; + } } /* diff --git a/src/pv/watchpid.c b/src/pv/watchpid.c index 2923e67..e20e3e5 100644 --- a/src/pv/watchpid.c +++ b/src/pv/watchpid.c @@ -418,7 +418,7 @@ static int pv_compare_watchfd(const void *a, const void *b) /* * Scan the given process and update the arrays with any new file - * descriptors. If "watch_fd" is not -1, then all other file descriptors + * descriptors. If "watch_fd" is not -1, then all other file descriptor * numbers will be ignored, * * Returns 0 on success, 1 if the process no longer exists or could not be @@ -494,6 +494,16 @@ int pv_watchpid_scanfds(pvstate_t state, pid_t watch_pid, int watch_fd, int *arr continue; if (info_array[check_idx].watch_fd != fd) continue; + if (info_array[check_idx].closed) { + /* + * If the fd is known but closed, + * immediately free it for re-use. + */ + info_array[check_idx].unused = true; + info_array[check_idx].displayable = false; + pv_freecontents_watchfd(&(info_array[check_idx])); + continue; + } found_idx = check_idx; break; } @@ -534,6 +544,7 @@ int pv_watchpid_scanfds(pvstate_t state, pid_t watch_pid, int watch_fd, int *arr pv_reset_watchfd(&(info_array[use_idx])); info_array[use_idx].watch_pid = watch_pid; info_array[use_idx].watch_fd = fd; + info_array[use_idx].closed = false; info_array[use_idx].unused = false; info_array[use_idx].displayable = true;