1 week bump. -- Scott Cheloha
> On Aug 5, 2017, at 8:25 PM, Scott Cheloha <scottchel...@gmail.com> wrote: > > Hi, > > In tetris(6) we use gettimeofday(2) to determine (roughly) how > long we polled for user input. This then gets subtracted from > the time remaining until we drop the block another row. > > This should be computed with a monotonic clock instead, lest > bullshit like clock drift rob you of that crucial tenth of a > second and cost you your new high score. > > Moving to a monotonic clock implies using nanoseconds instead of > microseconds, but this actually turns out to be kind of nice > because ppoll(2) accepts a timespec structure, so then we can > delete a few things we were using to jam a timeval into poll(2). > > I've playtested a bit and it doesn't ~feel~ any different. If > anything the game *should* feel less choppy under certain > conditions, though I can't really prove that. > > Feedback? > > -- > Scott Cheloha > > Index: games/tetris/input.c > =================================================================== > RCS file: /cvs/src/games/tetris/input.c,v > retrieving revision 1.18 > diff -u -p -r1.18 input.c > --- games/tetris/input.c 27 Aug 2016 02:02:44 -0000 1.18 > +++ games/tetris/input.c 6 Aug 2017 01:21:41 -0000 > @@ -40,89 +40,75 @@ > */ > > #include <sys/time.h> > + > #include <errno.h> > #include <poll.h> > +#include <time.h> > #include <unistd.h> > > #include "input.h" > #include "tetris.h" > > -/* return true iff the given timeval is positive */ > -#define TV_POS(tv) \ > - ((tv)->tv_sec > 0 || ((tv)->tv_sec == 0 && (tv)->tv_usec > 0)) > - > -/* subtract timeval `sub' from `res' */ > -#define TV_SUB(res, sub) \ > - (res)->tv_sec -= (sub)->tv_sec; \ > - (res)->tv_usec -= (sub)->tv_usec; \ > - if ((res)->tv_usec < 0) { \ > - (res)->tv_usec += 1000000; \ > - (res)->tv_sec--; \ > - } > +/* return true iff the given timespec is positive */ > +#define TS_POS(ts) \ > + ((ts)->tv_sec > 0 || ((ts)->tv_sec == 0 && (ts)->tv_nsec > 0)) > > /* > - * Do a `read wait': poll for reading from stdin, with timeout *tvp. > - * On return, modify *tvp to reflect the amount of time spent waiting. > + * Do a `read wait': poll for reading from stdin, with timeout *limit. > + * On return, subtract the time spent waiting from *limit. > * It will be positive only if input appeared before the time ran out; > * otherwise it will be zero or perhaps negative. > * > - * If tvp is nil, wait forever, but return if poll is interrupted. > + * If limit is NULL, wait forever, but return if poll is interrupted. > * > - * Return 0 => no input, 1 => can read() from stdin > + * Return 0 => no input, 1 => can read() from stdin, -1 => interrupted > */ > int > -rwait(struct timeval *tvp) > +rwait(struct timespec *limit) > { > - int timo = INFTIM; > - struct timeval starttv, endtv; > + struct timespec start, end, elapsed; > struct pollfd pfd[1]; > > -#define NILTZ ((struct timezone *)0) > - > - if (tvp) { > - (void) gettimeofday(&starttv, NILTZ); > - endtv = *tvp; > - timo = endtv.tv_sec * 1000 + endtv.tv_usec / 1000; > - } > -again: > pfd[0].fd = STDIN_FILENO; > pfd[0].events = POLLIN; > - switch (poll(pfd, 1, timo)) { > + > + if (limit != NULL) > + clock_gettime(CLOCK_MONOTONIC, &start); > +again: > + switch (ppoll(pfd, 1, limit, NULL)) { > case -1: > - if (tvp == 0) > + if (limit == NULL) > return (-1); > if (errno == EINTR) > goto again; > stop("poll failed, help"); > - > case 0: /* timed out */ > - tvp->tv_sec = 0; > - tvp->tv_usec = 0; > + timespecclear(limit); > return (0); > } > - if (tvp) { > - /* since there is input, we may not have timed out */ > - (void) gettimeofday(&endtv, NILTZ); > - TV_SUB(&endtv, &starttv); > - TV_SUB(tvp, &endtv); /* adjust *tvp by elapsed time */ > + if (limit != NULL) { > + /* we have input, so subtract the elapsed time from *limit */ > + clock_gettime(CLOCK_MONOTONIC, &end); > + timespecsub(&end, &start, &elapsed); > + timespecsub(limit, &elapsed, limit); > } > return (1); > } > > /* > - * `sleep' for the current turn time (using poll). > - * Eat any input that might be available. > + * `sleep' for the current turn time and eat any > + * input that becomes available. > */ > void > tsleep(void) > { > - struct timeval tv; > + struct timespec ts; > char c; > > - tv.tv_sec = 0; > - tv.tv_usec = fallrate; > - while (TV_POS(&tv)) > - if (rwait(&tv) && read(STDIN_FILENO, &c, 1) != 1) > + ts.tv_sec = 0; > + ts.tv_nsec = fallrate; > + while (TS_POS(&ts)) > + if (rwait(&ts) && read(STDIN_FILENO, &c, 1) != 1) > break; > } > > @@ -132,7 +118,7 @@ tsleep(void) > int > tgetchar(void) > { > - static struct timeval timeleft; > + static struct timespec timeleft; > char c; > > /* > @@ -144,10 +130,10 @@ tgetchar(void) > * > * Most of the hard work is done by rwait(). > */ > - if (!TV_POS(&timeleft)) { > + if (!TS_POS(&timeleft)) { > faster(); /* go faster */ > timeleft.tv_sec = 0; > - timeleft.tv_usec = fallrate; > + timeleft.tv_nsec = fallrate; > } > if (!rwait(&timeleft)) > return (-1); > Index: games/tetris/input.h > =================================================================== > RCS file: /cvs/src/games/tetris/input.h,v > retrieving revision 1.5 > diff -u -p -r1.5 input.h > --- games/tetris/input.h 3 Jun 2003 03:01:41 -0000 1.5 > +++ games/tetris/input.h 6 Aug 2017 01:21:41 -0000 > @@ -35,6 +35,6 @@ > * @(#)input.h 8.1 (Berkeley) 5/31/93 > */ > > -int rwait(struct timeval *); > +int rwait(struct timespec *); > int tgetchar(void); > void tsleep(void); > Index: games/tetris/tetris.c > =================================================================== > RCS file: /cvs/src/games/tetris/tetris.c,v > retrieving revision 1.31 > diff -u -p -r1.31 tetris.c > --- games/tetris/tetris.c 10 Jun 2016 13:07:07 -0000 1.31 > +++ games/tetris/tetris.c 6 Aug 2017 01:21:41 -0000 > @@ -197,7 +197,7 @@ main(int argc, char *argv[]) > if (argc) > usage(); > > - fallrate = 1000000 / level; > + fallrate = 1000000000L / level; > > for (i = 0; i <= 5; i++) { > for (j = i+1; j <= 5; j++) { > @@ -280,7 +280,7 @@ main(int argc, char *argv[]) > scr_msg(key_msg, 0); > scr_msg(msg, 1); > (void) fflush(stdout); > - } while (rwait((struct timeval *)NULL) == -1); > + } while (rwait(NULL) == -1); > scr_msg(msg, 0); > scr_msg(key_msg, 1); > place(curshape, pos, 0); > Index: games/tetris/tetris.h > =================================================================== > RCS file: /cvs/src/games/tetris/tetris.h,v > retrieving revision 1.11 > diff -u -p -r1.11 tetris.h > --- games/tetris/tetris.h 20 Nov 2015 07:40:23 -0000 1.11 > +++ games/tetris/tetris.h 6 Aug 2017 01:21:41 -0000 > @@ -135,15 +135,15 @@ extern const struct shape *nextshape; > /* > * Shapes fall at a rate faster than once per second. > * > - * The initial rate is determined by dividing 1 million microseconds > - * by the game `level'. (This is at most 1 million, or one second.) > - * Each time the fall-rate is used, it is decreased a little bit, > + * The initial rate is determined by dividing 1 billion nanoseconds > + * by the game `level'. (This is at most 1 billion, or one second.) > + * Each time the fallrate is used, it is decreased a little bit, > * depending on its current value, via the `faster' macro below. > * The value eventually reaches a limit, and things stop going faster, > * but by then the game is utterly impossible. > */ > -extern long fallrate; /* less than 1 million; smaller => faster */ > -#define faster() (fallrate -= fallrate / 3000) > +extern long fallrate; /* less than 1 billion; smaller => faster */ > +#define faster() (fallrate -= fallrate / 3000000) > > /* > * Game level must be between 1 and 9. This controls the initial fall rate