From 0dbdb862b1ace77546c3d58a9dd799bb1779756c Mon Sep 17 00:00:00 2001 From: Anthony DeRobertis Date: Tue, 24 Nov 2009 11:00:46 +1100 Subject: [PATCH] watch: add precision wait time option -p A patch from Debian. Bug-Debian: http://bugs.debian.org/183486 Reviewed-by: Craig Small Backported-by: Sami Kerola --- watch.1 | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++--- watch.c | 31 +++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/watch.1 b/watch.1 index cbfa3b32..99ffa6b3 100644 --- a/watch.1 +++ b/watch.1 @@ -4,7 +4,7 @@ watch \- execute a program periodically, showing output fullscreen .SH SYNOPSIS .na .B watch -.RB [ \-bdehvtx ] +.RB [ \-bdehpvtx ] .RB [ \-n .IR seconds ] .RB [ \-\-beep ] @@ -15,6 +15,7 @@ watch \- execute a program periodically, showing output fullscreen .RB [ \-\-help ] .RB [ \-\-interval=\fIseconds\fP] .RB [ \-\-no\-title ] +.RB [ \-\-precise ] .RB [ \-\-version ] .I command .SH DESCRIPTION @@ -28,7 +29,24 @@ every 2 seconds; use .B \-n or .B \-\-interval -to specify a different interval. +to specify a different interval. Normally, this interval is interpreted +as the amout of time between the completion of one run of +.I command +and the beginning of the next run. However, with the +.I \-p +or +.I \-\-precise +option, you can make +.BR watch +attempt to run +.I command +every +.I interval +seconds. Try it with +.B ntptime +and notice how the fractional seconds stays +(nearly) the same, as opposed to normal mode where they continuously +increase. .PP The .B \-d @@ -102,11 +120,21 @@ watch echo '$$' .br watch echo "'"'$$'"'" .PP +To see the effect of precision time keeping, try adding +.I \-p +to +.IP +watch \-n 10 sleep 1 +.PP You can watch for your administrator to install the latest kernel with .IP watch uname \-r .PP -(Just kidding.) +(Note that +.I \-p +isn't guaranteed to work across reboots, especially in the face of +.B ntpdate +or other bootup time-changing mechanisms) .SH BUGS Upon terminal resize, the screen will not be correctly repainted until the next scheduled update. All @@ -115,6 +143,22 @@ highlighting is lost on that update as well. .PP Non-printing characters are stripped from program output. Use "cat -v" as part of the command pipeline if you want to see them. +.PP +.I \-\-precise +mode doesn't yet have advanced temporal distortion technology to +compensate for a +.I command +that takes more than +.I interval +seconds to execute. +.B watch +also can get into a state where it rapid-fires as many executions of +.I command +as it can to catch up from a previous executions running longer than +.I interval +(for example, +.B netstat +taking ages on a DNS lookup). .SH AUTHORS The original .B watch @@ -122,3 +166,7 @@ was written by Tony Rems in 1991, with mods and corrections by Francois Pinard. It was reworked and new features added by Mike Coleman in 1999. The beep, exec, and error handling features were added by Morty Abzug in 2008. +On a not so dark and stormy morning +in March of 2003, Anthony DeRobertis got sick of +his watches that should update every minute eventually updating many +seconds after the minute started, and added microsecond precision. diff --git a/watch.c b/watch.c index bfd15e68..cf407925 100644 --- a/watch.c +++ b/watch.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -39,13 +40,14 @@ static struct option longopts[] = { {"beep", no_argument, 0, 'b'}, {"errexit", no_argument, 0, 'e'}, {"exec", no_argument, 0, 'x'}, + {"precise", no_argument, 0, 'p'}, {"no-title", no_argument, 0, 't'}, {"version", no_argument, 0, 'v'}, {0, 0, 0, 0} }; static char usage[] = - "Usage: %s [-bdhntvx] [--beep] [--color] [--differences[=cumulative]] [--exec] [--help] [--interval=] [--no-title] [--version] \n"; + "Usage: %s [-bdhnptvx] [--beep] [--color] [--differences[=cumulative]] [--exec] [--help] [--interval=] [--no-title] [--version] \n"; static char *progname; @@ -54,6 +56,7 @@ static int height = 24, width = 80; static int screen_size_changed = 0; static int first_screen = 1; static int show_title = 2; // number of lines used, 2 or 0 +static int precise_timekeeping = 0; #define min(x,y) ((x) > (y) ? (y) : (x)) #define MAX_ANSIBUF 10 @@ -206,6 +209,15 @@ get_terminal_size(void) } } +/* get current time in usec */ +typedef unsigned long long watch_usec_t; +#define USECS_PER_SEC (1000000ull) +watch_usec_t get_time_usec() { + struct timeval now; + gettimeofday(&now, NULL); + return USECS_PER_SEC*now.tv_sec + now.tv_usec; +} + int main(int argc, char *argv[]) { @@ -221,6 +233,8 @@ main(int argc, char *argv[]) char *command; char **command_argv; int command_length = 0; /* not including final \0 */ + watch_usec_t next_loop; /* next loop time in us, used for precise time + keeping only */ int pipefd[2]; int status; pid_t child; @@ -228,7 +242,7 @@ main(int argc, char *argv[]) setlocale(LC_ALL, ""); progname = argv[0]; - while ((optc = getopt_long(argc, argv, "+bced::hn:vtx", longopts, (int *) 0)) + while ((optc = getopt_long(argc, argv, "+bced::hn:pvtx", longopts, (int *) 0)) != EOF) { switch (optc) { case 'b': @@ -266,6 +280,9 @@ main(int argc, char *argv[]) interval = ~0u/1000000; } break; + case 'p': + precise_timekeeping = 1; + break; case 'v': option_version = 1; break; @@ -289,6 +306,7 @@ main(int argc, char *argv[]) fputs(" -e, --errexit\t\t\t\texit watch if the command has a non-zero exit\n", stderr); fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr); fputs(" -n, --interval=\t\tseconds to wait between updates\n", stderr); + fputs(" -p, --precise\t\t\t\tprecise timing, ignore command run time\n", stderr); fputs(" -v, --version\t\t\t\tprint the version number\n", stderr); fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr); fputs(" -x, --exec\t\t\t\tpass command to exec instead of sh\n", stderr); @@ -336,6 +354,9 @@ main(int argc, char *argv[]) noecho(); cbreak(); + if (precise_timekeeping) + next_loop = get_time_usec(); + for (;;) { time_t t = time(NULL); char *ts = ctime(&t); @@ -486,6 +507,12 @@ main(int argc, char *argv[]) first_screen = 0; refresh(); + if (precise_timekeeping) { + watch_usec_t cur_time = get_time_usec(); + next_loop += USECS_PER_SEC*interval; + if (cur_time < next_loop) + usleep(next_loop - cur_time); + } else usleep(interval * 1000000); }