1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-12-28 04:05:12 +01:00

53257: use monotonic clock where appropriate

update the following features to use the monotonic clock for calculating time
deltas and intervals:

* MAILCHECK parameter
* PERIOD parameter
* SECONDS parameter
* %(nS.t.f) prompt-expansion sequence
* time built-in's elapsed time and cpu % values
* zsh/zftp ZFTP_TMOUT parameter
* zsh/zprof timings

also use CLOCK_MONOTONIC_RAW instead of CLOCK_MONOTONIC on macOS
This commit is contained in:
dana 2024-12-26 09:36:45 -06:00
parent 7798fd88ac
commit 6bb792dba8
18 changed files with 225 additions and 92 deletions

View file

@ -1,3 +1,12 @@
2024-12-26 dana <dana@dana.is>
* 53257: Doc/Zsh/params.yo, Doc/Zsh/prompt.yo,
Src/Modules/zftp.c, Src/Modules/zprof.c, Src/compat.c,
Src/exec.c, Src/hist.c, Src/init.c, Src/jobs.c, Src/params.c,
Src/prompt.c, Src/signals.c, Src/utils.c, Src/zsh.h,
Test/A08time.ztst, Test/D01prompt.ztst, Test/D04parameter.ztst:
use monotonic clock where appropriate
2024-12-16 dana <dana@dana.is>
* 53251: Completion/Unix/Command/_man: fix page completion on

View file

@ -926,7 +926,9 @@ referenced or seeded in the parent shell in between subshell invocations.
)
vindex(SECONDS)
item(tt(SECONDS) <S>)(
The number of seconds since shell invocation. If this parameter
The number of seconds since shell invocation. On most platforms, this
is a monotonic value, so it is not affected by NTP time jumps or other
clock changes (though it may be affected by slewing). If this parameter
is assigned a value, then the value returned upon reference
will be the value that was assigned plus the number of seconds
since the assignment.
@ -936,8 +938,10 @@ be changed using the tt(typeset) command. The type may be changed only
to one of the floating point types or back to integer. For example,
`tt(typeset -F SECONDS)'
causes the value to be reported as a floating point number. The
value is available to microsecond accuracy, although the shell may
show more or fewer digits depending on the use of tt(typeset). See
value is nominally available to nanosecond precision, although this
varies by platform (and probably isn't accurate to 1 ns regardless),
and the shell may show more or fewer digits depending on the
use of tt(typeset). See
the documentation for the builtin tt(typeset) in
ifzman(zmanref(zshbuiltins))\
ifnzman(noderef(Shell Builtin Commands)) for more details.
@ -1735,8 +1739,12 @@ A star may be inserted between the percent sign and flags printing time
(e.g., `tt(%*E)'); this causes the time to be printed in
`var(hh)tt(:)var(mm)tt(:)var(ss)tt(.)var(ttt)'
format (hours and minutes are only printed if they are not zero).
Alternatively, `tt(m)' or `tt(u)' may be used (e.g., `tt(%mE)') to produce
time output in milliseconds or microseconds, respectively.
Alternatively, `tt(m)', `tt(u)', or `tt(n)' may be used (e.g.,
`tt(%mE)') to produce time output in milliseconds, microseconds, or
nanoseconds, respectively. Note that some timings on some platforms
are not actually nanosecond-precise (nor accurate to 1 ns when
they are); in fact on many systems user and kernel times are not
even microsecond-precise.
)
vindex(TMOUT)
item(tt(TMOUT))(

View file

@ -195,7 +195,8 @@ sitem(tt(%K))(the hour of the day on the 24-hour clock)
sitem(tt(%L))(the hour of the day on the 12-hour clock)
endsitem()
In addition, if the system supports the POSIX tt(gettimeofday) system
In addition, if the system supports the POSIX tt(clock_gettime)
or tt(gettimeofday) system
call, tt(%.) provides decimal fractions of a second since the epoch with
leading zeroes. By default three decimal places are provided, but a
number of digits up to 9 may be given following the tt(%); hence tt(%6.)

View file

@ -397,7 +397,7 @@ zfalarm(int tmout)
signal(SIGALRM, zfhandler);
oalremain = alarm(tmout);
if (oalremain)
oaltime = time(NULL);
oaltime = zmonotime(NULL);
/*
* We'll leave sigtrapped, sigfuncs and TRAPXXX as they are; since the
* shell's handler doesn't get the signal, they don't matter.
@ -431,7 +431,7 @@ zfunalarm(void)
* I love the way alarm() uses unsigned int while time_t
* is probably something completely different.
*/
unsigned int tdiff = time(NULL) - oaltime;
time_t tdiff = zmonotime(NULL) - oaltime;
alarm(oalremain < tdiff ? 1 : oalremain - tdiff);
} else
alarm(0);

View file

@ -239,8 +239,7 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name)
struct sfunc sf, *sp;
Pfunc f = NULL;
Parc a = NULL;
struct timeval tv;
struct timezone dummy;
struct timespec ts;
double prev = 0, now;
char *name_for_lookups;
@ -278,19 +277,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name)
stack = &sf;
f->calls++;
tv.tv_sec = tv.tv_usec = 0;
gettimeofday(&tv, &dummy);
sf.beg = prev = ((((double) tv.tv_sec) * 1000.0) +
(((double) tv.tv_usec) / 1000.0));
ts.tv_sec = ts.tv_nsec = 0;
zgettime_monotonic_if_available(&ts);
sf.beg = prev = ((((double) ts.tv_sec) * 1000.0) +
(((double) ts.tv_nsec) / 1000000.0));
}
runshfunc(prog, w, name);
if (active) {
if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) {
tv.tv_sec = tv.tv_usec = 0;
gettimeofday(&tv, &dummy);
ts.tv_sec = ts.tv_nsec = 0;
zgettime_monotonic_if_available(&ts);
now = ((((double) tv.tv_sec) * 1000.0) +
(((double) tv.tv_usec) / 1000.0));
now = ((((double) ts.tv_sec) * 1000.0) +
(((double) ts.tv_nsec) / 1000000.0));
f->self += now - sf.beg;
for (sp = sf.prev; sp && sp->p != f; sp = sp->prev);
if (!sp)

View file

@ -136,7 +136,19 @@ zgettime_monotonic_if_available(struct timespec *ts)
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
struct timespec dts;
/*
* On at least some versions of macOS it appears that CLOCK_MONOTONIC is not
* actually monotonic -- there are reports that it can go backwards.
* CLOCK_MONOTONIC_RAW does not have this problem. On top of that, it is faster
* to read and it has nanosecond precision. We could use it on other systems
* too, but on Linux at least it seems that CLOCK_MONOTONIC is preferred
*/
#if defined(__APPLE__) && defined(CLOCK_MONOTONIC_RAW)
if (clock_gettime(CLOCK_MONOTONIC_RAW, &dts) < 0) {
#else
if (clock_gettime(CLOCK_MONOTONIC, &dts) < 0) {
#endif
zwarn("unable to retrieve CLOCK_MONOTONIC time: %e", errno);
ret--;
} else {

View file

@ -348,10 +348,9 @@ setlimits(char *nam)
/**/
static pid_t
zfork(struct timeval *tv)
zfork(struct timespec *ts)
{
pid_t pid;
struct timezone dummy_tz;
/*
* Is anybody willing to explain this test?
@ -360,8 +359,8 @@ zfork(struct timeval *tv)
zerr("job table full");
return -1;
}
if (tv)
gettimeofday(tv, &dummy_tz);
if (ts)
zgettime_monotonic_if_available(ts);
/*
* Queueing signals is necessary on Linux because fork()
* manipulates mutexes, leading to deadlock in memory
@ -460,7 +459,7 @@ zfork(struct timeval *tv)
int list_pipe = 0, simple_pline = 0;
static pid_t list_pipe_pid;
static struct timeval list_pipe_start;
static struct timespec list_pipe_start;
static int nowait, pline_level = 0;
static int list_pipe_child = 0, list_pipe_job;
static char list_pipe_text[JOBTEXTSIZE];
@ -1863,7 +1862,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
(jobtab[list_pipe_job].stat & STAT_STOPPED)))) {
pid_t pid = 0;
int synch[2];
struct timeval bgtime;
struct timespec bgtime;
/*
* A pipeline with the shell handling the right
@ -2284,7 +2283,7 @@ closemn(struct multio **mfds, int fd, int type)
char buf[TCBUFSIZE];
int len, i;
pid_t pid;
struct timeval bgtime;
struct timespec bgtime;
/*
* We need to block SIGCHLD in case the process
@ -2829,7 +2828,7 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc,
pid_t pid;
int synch[2], flags;
struct entersubsh_ret esret;
struct timeval bgtime;
struct timespec bgtime;
child_block();
esret.gleader = -1;
@ -2947,7 +2946,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
* for the "time" keyword
*/
child_times_t shti, chti;
struct timeval then;
struct timespec then;
if (how & Z_TIMED)
shelltime(&shti, &chti, &then, 0);
@ -5060,7 +5059,7 @@ getproc(char *cmd, char **eptr)
int out = *cmd == Inang;
char *pnam;
pid_t pid;
struct timeval bgtime;
struct timespec bgtime;
#ifndef PATH_DEV_FD
int fd;
@ -5149,7 +5148,7 @@ getpipe(char *cmd, int nullexec)
Eprog prog;
int pipes[2], out = *cmd == Inang;
pid_t pid;
struct timeval bgtime;
struct timespec bgtime;
char *ends;
if (!(prog = parsecmd(cmd, &ends)))

View file

@ -2891,9 +2891,9 @@ flockhistfile(char *fn, int keep_trying)
/*
* Timeout is ten seconds.
*/
end_time = time(NULL) + (time_t)10;
end_time = zmonotime(NULL) + (time_t)10;
while (fcntl(flock_fd, F_SETLKW, &lck) == -1) {
if (!keep_trying || time(NULL) >= end_time ||
if (!keep_trying || zmonotime(NULL) >= end_time ||
/*
* Randomise wait to minimise clashes with shells exiting at
* the same time.
@ -3137,7 +3137,7 @@ static int lockhistct;
static int
checklocktime(char *lockfile, long *sleep_usp, time_t then)
{
time_t now = time(NULL);
time_t now = zmonotime(NULL);
if (now + 10 < then) {
/* File is more than 10 seconds in the future? */

View file

@ -1022,7 +1022,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
#ifdef USE_GETPWUID
struct passwd *pswd;
#endif
struct timezone dummy_tz;
char *ptr;
int i, j;
#if defined(SITEFPATH_DIR) || defined(FPATH_DIR) || defined (ADDITIONAL_FPATH) || defined(FIXED_FPATH_DIR)
@ -1109,8 +1108,8 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
hatchar = '^';
termflags = TERM_UNKNOWN;
curjob = prevjob = coprocin = coprocout = -1;
gettimeofday(&shtimer, &dummy_tz); /* init $SECONDS */
srand((unsigned int)(shtimer.tv_sec + shtimer.tv_usec)); /* seed $RANDOM */
zgettime_monotonic_if_available(&shtimer); /* init $SECONDS */
srand((unsigned int)(shtimer.tv_sec + shtimer.tv_nsec)); /* seed $RANDOM */
/* Set default path */
path = (char **) zalloc(sizeof(*path) * 5);
@ -1297,7 +1296,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
#endif
breaks = loops = 0;
lastmailcheck = time(NULL);
lastmailcheck = zmonotime(NULL);
locallevel = sourcelevel = 0;
sfcontext = SFC_NONE;
trap_return = 0;

View file

@ -136,7 +136,7 @@ int numpipestats, pipestats[MAX_PIPESTATS];
/**/
static struct timeval *
dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2)
dtime_tv(struct timeval *dt, struct timeval *t1, struct timeval *t2)
{
dt->tv_sec = t2->tv_sec - t1->tv_sec;
dt->tv_usec = t2->tv_usec - t1->tv_usec;
@ -147,6 +147,21 @@ dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2)
return dt;
}
/* As above, but with timespecs */
/**/
static struct timespec *
dtime_ts(struct timespec *dt, struct timespec *t1, struct timespec *t2)
{
dt->tv_sec = t2->tv_sec - t1->tv_sec;
dt->tv_nsec = t2->tv_nsec - t1->tv_nsec;
if (dt->tv_nsec < 0) {
dt->tv_nsec += 1000000000.0;
dt->tv_sec -= 1.0;
}
return dt;
}
/* change job table entry from stopped to running */
/**/
@ -349,7 +364,6 @@ get_usage(void)
void
update_process(Process pn, int status)
{
struct timezone dummy_tz;
#ifdef HAVE_GETRUSAGE
struct timeval childs = child_usage.ru_stime;
struct timeval childu = child_usage.ru_utime;
@ -360,12 +374,12 @@ update_process(Process pn, int status)
/* get time-accounting info */
get_usage();
gettimeofday(&pn->endtime, &dummy_tz); /* record time process exited */
zgettime_monotonic_if_available(&pn->endtime); /* record time process exited */
pn->status = status; /* save the status returned by WAIT */
#ifdef HAVE_GETRUSAGE
dtime(&pn->ti.ru_stime, &childs, &child_usage.ru_stime);
dtime(&pn->ti.ru_utime, &childu, &child_usage.ru_utime);
dtime_tv(&pn->ti.ru_stime, &childs, &child_usage.ru_stime);
dtime_tv(&pn->ti.ru_utime, &childu, &child_usage.ru_utime);
#else
pn->ti.st = shtms.tms_cstime - childs; /* compute process system space time */
pn->ti.ut = shtms.tms_cutime - childu; /* compute process user space time */
@ -753,7 +767,7 @@ printhhmmss(double secs)
}
static void
printtime(struct timeval *real, child_times_t *ti, char *desc)
printtime(struct timespec *real, child_times_t *ti, char *desc)
{
char *s;
double elapsed_time, user_time, system_time;
@ -774,21 +788,21 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
}
/* go ahead and compute these, since almost every TIMEFMT will have them */
elapsed_time = real->tv_sec + real->tv_usec / 1000000.0;
elapsed_time = real->tv_sec + real->tv_nsec / 1000000000.0;
#ifdef HAVE_GETRUSAGE
user_time = ti->ru_utime.tv_sec + ti->ru_utime.tv_usec / 1000000.0;
system_time = ti->ru_stime.tv_sec + ti->ru_stime.tv_usec / 1000000.0;
total_time = user_time + system_time;
percent = 100.0 * total_time
/ (real->tv_sec + real->tv_usec / 1000000.0);
/ (real->tv_sec + real->tv_nsec / 1000000000.0);
#else
{
long clktck = get_clktck();
user_time = ti->ut / (double) clktck;
system_time = ti->st / (double) clktck;
percent = 100.0 * (ti->ut + ti->st)
/ (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0);
/ (clktck * real->tv_sec + clktck * real->tv_nsec / 1000000000.0);
}
#endif
@ -844,6 +858,23 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
break;
}
break;
case 'n':
switch (*++s) {
case 'E':
fprintf(stderr, "%0.fns", elapsed_time * 1000000000.0);
break;
case 'U':
fprintf(stderr, "%0.fns", user_time * 1000000000.0);
break;
case 'S':
fprintf(stderr, "%0.fns", system_time * 1000000000.0);
break;
default:
fprintf(stderr, "%%n");
s--;
break;
}
break;
case '*':
switch (*++s) {
case 'E':
@ -991,12 +1022,12 @@ static void
dumptime(Job jn)
{
Process pn;
struct timeval dtimeval;
struct timespec dtimespec;
if (!jn->procs)
return;
for (pn = jn->procs; pn; pn = pn->next)
printtime(dtime(&dtimeval, &pn->bgtime, &pn->endtime), &pn->ti,
printtime(dtime_ts(&dtimespec, &pn->bgtime, &pn->endtime), &pn->ti,
pn->text);
}
@ -1506,7 +1537,7 @@ deletejob(Job jn, int disowning)
/**/
void
addproc(pid_t pid, char *text, int aux, struct timeval *bgtime,
addproc(pid_t pid, char *text, int aux, struct timespec *bgtime,
int gleader, int list_pipe_job_used)
{
Process pn, *pnlist;
@ -1894,16 +1925,15 @@ spawnjob(void)
/**/
void
shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int delta)
shelltime(child_times_t *shell, child_times_t *kids, struct timespec *then, int delta)
{
struct timezone dummy_tz;
struct timeval dtimeval, now;
struct timespec dtimespec, now;
child_times_t ti;
#ifndef HAVE_GETRUSAGE
struct tms buf;
#endif
gettimeofday(&now, &dummy_tz);
zgettime_monotonic_if_available(&now);
#ifdef HAVE_GETRUSAGE
getrusage(RUSAGE_SELF, &ti);
@ -1916,8 +1946,8 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d
if (shell) {
if (delta) {
#ifdef HAVE_GETRUSAGE
dtime(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime);
dtime(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime);
dtime_tv(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime);
dtime_tv(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime);
#else
ti.ut -= shell->ut;
ti.st -= shell->st;
@ -1926,15 +1956,15 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d
*shell = ti;
}
if (delta)
dtime(&dtimeval, then, &now);
dtime_ts(&dtimespec, then, &now);
else {
if (then)
*then = now;
dtime(&dtimeval, &shtimer, &now);
dtime_ts(&dtimespec, &shtimer, &now);
}
if (!delta == !shell)
printtime(&dtimeval, &ti, "shell");
printtime(&dtimespec, &ti, "shell");
#ifdef HAVE_GETRUSAGE
getrusage(RUSAGE_CHILDREN, &ti);
@ -1945,8 +1975,8 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d
if (kids) {
if (delta) {
#ifdef HAVE_GETRUSAGE
dtime(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime);
dtime(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime);
dtime_tv(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime);
dtime_tv(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime);
#else
ti.ut -= shell->ut;
ti.st -= shell->st;
@ -1955,7 +1985,7 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d
*kids = ti;
}
if (!delta == !kids)
printtime(&dtimeval, &ti, "children");
printtime(&dtimespec, &ti, "children");
}
/* see if jobs need printing */

View file

@ -137,11 +137,11 @@ unsigned char hatchar, hashchar;
unsigned char keyboardhackchar = '\0';
/* $SECONDS = now.tv_sec - shtimer.tv_sec
* + (now.tv_usec - shtimer.tv_usec) / 1000000.0
* + (now.tv_nsec - shtimer.tv_nsec) / 1000000000.0
* (rounded to an integer if the parameter is not set to float) */
/**/
struct timeval shtimer;
struct timespec shtimer;
/* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */
@ -4496,13 +4496,12 @@ randomsetfn(UNUSED(Param pm), zlong v)
zlong
intsecondsgetfn(UNUSED(Param pm))
{
struct timeval now;
struct timezone dummy_tz;
struct timespec now;
gettimeofday(&now, &dummy_tz);
zgettime_monotonic_if_available(&now);
return (zlong)(now.tv_sec - shtimer.tv_sec -
(now.tv_usec < shtimer.tv_usec ? 1 : 0));
(now.tv_nsec < shtimer.tv_nsec ? 1 : 0));
}
/* Function to set value of special parameter `SECONDS' */
@ -4511,48 +4510,47 @@ intsecondsgetfn(UNUSED(Param pm))
void
intsecondssetfn(UNUSED(Param pm), zlong x)
{
struct timeval now;
struct timezone dummy_tz;
struct timespec now;
zlong diff;
gettimeofday(&now, &dummy_tz);
zgettime_monotonic_if_available(&now);
diff = (zlong)now.tv_sec - x;
shtimer.tv_sec = diff;
if ((zlong)shtimer.tv_sec != diff)
zwarn("SECONDS truncated on assignment");
shtimer.tv_usec = now.tv_usec;
shtimer.tv_nsec = now.tv_nsec;
}
/**/
double
floatsecondsgetfn(UNUSED(Param pm))
{
struct timeval now;
struct timezone dummy_tz;
struct timespec now;
gettimeofday(&now, &dummy_tz);
zgettime_monotonic_if_available(&now);
return (double)(now.tv_sec - shtimer.tv_sec) +
(double)(now.tv_usec - shtimer.tv_usec) / 1000000.0;
(double)(now.tv_nsec - shtimer.tv_nsec) / 1000000000.0;
}
/**/
void
floatsecondssetfn(UNUSED(Param pm), double x)
{
struct timeval now;
struct timezone dummy_tz;
struct timespec now;
zgettime_monotonic_if_available(&now);
gettimeofday(&now, &dummy_tz);
shtimer.tv_sec = now.tv_sec - (zlong)x;
shtimer.tv_usec = now.tv_usec - (zlong)((x - (zlong)x) * 1000000.0);
shtimer.tv_nsec = now.tv_nsec - (zlong)((x - (zlong)x) * 1000000000.0);
}
/**/
double
getrawseconds(void)
{
return (double)shtimer.tv_sec + (double)shtimer.tv_usec / 1000000.0;
return (double)shtimer.tv_sec + (double)shtimer.tv_nsec / 1000000000.0;
}
/**/
@ -4560,7 +4558,7 @@ void
setrawseconds(double x)
{
shtimer.tv_sec = (zlong)x;
shtimer.tv_usec = (zlong)((x - (zlong)x) * 1000000.0);
shtimer.tv_nsec = (zlong)((x - (zlong)x) * 1000000000.0);
}
/**/

View file

@ -469,7 +469,7 @@ putpromptchar(int doprint, int endchar)
test = 1;
break;
case 'S':
if (time(NULL) - shtimer.tv_sec >= arg)
if (zmonotime(NULL) - shtimer.tv_sec >= arg)
test = 1;
break;
case 'v':

View file

@ -342,8 +342,7 @@ wait_for_processes(void)
zwarn("job can't be suspended");
} else {
#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE)
struct timezone dummy_tz;
gettimeofday(&pn->endtime, &dummy_tz);
zgettime_monotonic_if_available(&pn->endtime);
#ifdef WIFCONTINUED
if (WIFCONTINUED(status))
pn->status = SP_RUNNING;

View file

@ -1570,14 +1570,14 @@ preprompt(void)
/* If 1) the parameter PERIOD exists, 2) a hook function for *
* "periodic" exists, 3) it's been greater than PERIOD since we *
* executed any such hook, then execute it now. */
if (period && ((zlong)time(NULL) > (zlong)lastperiodic + period) &&
if (period && ((zlong)zmonotime(NULL) > (zlong)lastperiodic + period) &&
!callhookfunc("periodic", NULL, 1, NULL))
lastperiodic = time(NULL);
lastperiodic = zmonotime(NULL);
if (errflag)
return;
/* Check mail */
currentmailcheck = time(NULL);
currentmailcheck = zmonotime(NULL);
if (mailcheck &&
(zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) {
char *mailfile;
@ -2761,6 +2761,19 @@ timespec_diff_us(const struct timespec *t1, const struct timespec *t2)
return (reverse ? LONG_MIN : LONG_MAX);
}
/* Like time(), but uses the monotonic clock */
/**/
mod_export int
zmonotime(time_t *tloc)
{
struct timespec ts;
zgettime_monotonic_if_available(&ts);
if (tloc)
*tloc = ts.tv_sec;
return ts.tv_sec;
}
/*
* Sleep for the given number of microseconds --- must be within
* range of a long at the moment, but this is only used for
@ -2794,7 +2807,9 @@ zsleep(long us)
/**
* Sleep for time (fairly) randomly up to max_us microseconds.
* Don't let the wallclock time extend beyond end_time.
* Don't let the time extend beyond end_time. end_time is compared to
* the current *monotonic* clock time, so do NOT base it on e.g. time(2);
* use zmonotime() or zgettime_monotonic_if_available().
* Return 1 if that seemed to work, else 0.
*
* For best results max_us should be a multiple of 2**16 or large
@ -2806,7 +2821,7 @@ int
zsleep_random(long max_us, time_t end_time)
{
long r;
time_t now = time(NULL);
time_t now = zmonotime(NULL);
/*
* Randomish backoff. Doesn't need to be fundamentally

View file

@ -1115,8 +1115,8 @@ struct process {
char text[JOBTEXTSIZE]; /* text to print when 'jobs' is run */
int status; /* return code from waitpid/wait3() */
child_times_t ti;
struct timeval bgtime; /* time job was spawned */
struct timeval endtime; /* time job exited */
struct timespec bgtime; /* time job was spawned */
struct timespec endtime; /* time job exited */
};
struct execstack {

View file

@ -11,9 +11,44 @@
(time cat) >&/dev/null
0:`time' keyword (status only)
( TIMEFMT='%E %mE %uE %* %m%mm %u%uu'; time (:) )
( TIMEFMT='%E %mE %uE %nE %* %m%mm %u%uu %n%nn'; time (:) )
0:`time' keyword with custom TIMEFMT
*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us %\* %m%mm %u%uu
*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us [0-9]##ns %\* %m%mm %u%uu %n%nn
( TIMEFMT='x %U %S %E'; time (:) )
0:TIMEFMT %[USE] use centisecond precision
*?x( <0-9>.<00-99>s)(#c3)
( TIMEFMT='x %*U %*S %*E'; time (:) )
0:TIMEFMT %*[USE] use millisecond precision
*?x( <0-9>.<000-999>)(#c3)
( TIMEFMT='%nU %nS'; time (read -k3 -t0.1) )
1:TIMEFMT %nU and %nS are limited to microsecond precision
*?[1-9][0-9]#000ns [1-9][0-9]#000ns
# SECONDS (after - before) must be greater than the elapsed time, but not much
# greater. 25% was picked arbitrarily as something that hopefully will prevent
# the test from failing on slow machines
(
typeset -F SECONDS
TIMEFMT=%nE
a=$SECONDS
t=$( (time (read -k3 -t0.1)) 2>&1 )
b=$SECONDS
s=$(( b - a ))
t=$(( ${t%ns}.0 / 10**9 ))
echo $s $t $(( s > t )) $(( t > s - (s * 0.25) ))
)
0:`time' elapsed time matches SECONDS
*>[0-9.]## [0-9.]## 1 1
# Again, the wide range here is an attempt to prevent this test from failing on
# slow machines. We don't care about the exact time, just that it's vaguely sane
# and that each representation has the same basis
( TIMEFMT='%E %mE %uE %nE %*E'; time (read -k3 -t0.1) )
1:TIMEFMT elapsed time values
*?0.<10-50>s <10-500>ms <100000-500000>us <100000000-500000000>ns 0.<100-500>
time x=1
0:`time' simple assignment

View file

@ -68,6 +68,13 @@
print -P '%(?.true.false)'
0:ternary prompt escapes
>true
>false
sec=$SECONDS
eval "print -P '%(${sec}S.true.false)'"
eval "print -P '%($((sec+30))S.true.false)'"
0:ternary prompt escape with test character S
>true
>false
print -P 'start %10<...<truncated at 10%<< Not truncated%3< ...<Not shown'

View file

@ -1550,6 +1550,28 @@
>1
>1
# Integer
a=$SECONDS
sleep 1
b=$SECONDS
print -r - $a $b $(( b > a ))
# Float
typeset -F SECONDS
a=$SECONDS
repeat 10 :
b=$SECONDS
print -r - $a $b $(( b > a ))
# Assignment
a=$SECONDS
SECONDS=8888
repeat 10 :
b=$SECONDS
print -r - $(( a < 8888 )) $(( b > 8888 ))
0:SECONDS
*>[0-9]## [0-9]## 1
*>[0-9]##.[0-9]## [0-9]##.[0-9]## 1
*>1 1
foo=("|" "?")
[[ "|" = ${(j.|.)foo} ]] && print yes || print no
[[ "|" = ${(j.|.)~foo} ]] && print yes || print no