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:
parent
7798fd88ac
commit
6bb792dba8
18 changed files with 225 additions and 92 deletions
|
@ -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
|
||||
|
|
|
@ -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))(
|
||||
|
|
|
@ -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.)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
12
Src/compat.c
12
Src/compat.c
|
@ -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 {
|
||||
|
|
21
Src/exec.c
21
Src/exec.c
|
@ -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)))
|
||||
|
|
|
@ -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? */
|
||||
|
|
|
@ -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;
|
||||
|
|
78
Src/jobs.c
78
Src/jobs.c
|
@ -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 */
|
||||
|
|
38
Src/params.c
38
Src/params.c
|
@ -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);
|
||||
}
|
||||
|
||||
/**/
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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;
|
||||
|
|
25
Src/utils.c
25
Src/utils.c
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue