mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-12-28 16:15:02 +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>
|
2024-12-16 dana <dana@dana.is>
|
||||||
|
|
||||||
* 53251: Completion/Unix/Command/_man: fix page completion on
|
* 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)
|
vindex(SECONDS)
|
||||||
item(tt(SECONDS) <S>)(
|
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
|
is assigned a value, then the value returned upon reference
|
||||||
will be the value that was assigned plus the number of seconds
|
will be the value that was assigned plus the number of seconds
|
||||||
since the assignment.
|
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,
|
to one of the floating point types or back to integer. For example,
|
||||||
`tt(typeset -F SECONDS)'
|
`tt(typeset -F SECONDS)'
|
||||||
causes the value to be reported as a floating point number. The
|
causes the value to be reported as a floating point number. The
|
||||||
value is available to microsecond accuracy, although the shell may
|
value is nominally available to nanosecond precision, although this
|
||||||
show more or fewer digits depending on the use of tt(typeset). See
|
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
|
the documentation for the builtin tt(typeset) in
|
||||||
ifzman(zmanref(zshbuiltins))\
|
ifzman(zmanref(zshbuiltins))\
|
||||||
ifnzman(noderef(Shell Builtin Commands)) for more details.
|
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
|
(e.g., `tt(%*E)'); this causes the time to be printed in
|
||||||
`var(hh)tt(:)var(mm)tt(:)var(ss)tt(.)var(ttt)'
|
`var(hh)tt(:)var(mm)tt(:)var(ss)tt(.)var(ttt)'
|
||||||
format (hours and minutes are only printed if they are not zero).
|
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
|
Alternatively, `tt(m)', `tt(u)', or `tt(n)' may be used (e.g.,
|
||||||
time output in milliseconds or microseconds, respectively.
|
`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)
|
vindex(TMOUT)
|
||||||
item(tt(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)
|
sitem(tt(%L))(the hour of the day on the 12-hour clock)
|
||||||
endsitem()
|
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
|
call, tt(%.) provides decimal fractions of a second since the epoch with
|
||||||
leading zeroes. By default three decimal places are provided, but a
|
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.)
|
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);
|
signal(SIGALRM, zfhandler);
|
||||||
oalremain = alarm(tmout);
|
oalremain = alarm(tmout);
|
||||||
if (oalremain)
|
if (oalremain)
|
||||||
oaltime = time(NULL);
|
oaltime = zmonotime(NULL);
|
||||||
/*
|
/*
|
||||||
* We'll leave sigtrapped, sigfuncs and TRAPXXX as they are; since the
|
* We'll leave sigtrapped, sigfuncs and TRAPXXX as they are; since the
|
||||||
* shell's handler doesn't get the signal, they don't matter.
|
* 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
|
* I love the way alarm() uses unsigned int while time_t
|
||||||
* is probably something completely different.
|
* is probably something completely different.
|
||||||
*/
|
*/
|
||||||
unsigned int tdiff = time(NULL) - oaltime;
|
time_t tdiff = zmonotime(NULL) - oaltime;
|
||||||
alarm(oalremain < tdiff ? 1 : oalremain - tdiff);
|
alarm(oalremain < tdiff ? 1 : oalremain - tdiff);
|
||||||
} else
|
} else
|
||||||
alarm(0);
|
alarm(0);
|
||||||
|
|
|
@ -239,8 +239,7 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name)
|
||||||
struct sfunc sf, *sp;
|
struct sfunc sf, *sp;
|
||||||
Pfunc f = NULL;
|
Pfunc f = NULL;
|
||||||
Parc a = NULL;
|
Parc a = NULL;
|
||||||
struct timeval tv;
|
struct timespec ts;
|
||||||
struct timezone dummy;
|
|
||||||
double prev = 0, now;
|
double prev = 0, now;
|
||||||
char *name_for_lookups;
|
char *name_for_lookups;
|
||||||
|
|
||||||
|
@ -278,19 +277,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name)
|
||||||
stack = &sf;
|
stack = &sf;
|
||||||
|
|
||||||
f->calls++;
|
f->calls++;
|
||||||
tv.tv_sec = tv.tv_usec = 0;
|
ts.tv_sec = ts.tv_nsec = 0;
|
||||||
gettimeofday(&tv, &dummy);
|
zgettime_monotonic_if_available(&ts);
|
||||||
sf.beg = prev = ((((double) tv.tv_sec) * 1000.0) +
|
sf.beg = prev = ((((double) ts.tv_sec) * 1000.0) +
|
||||||
(((double) tv.tv_usec) / 1000.0));
|
(((double) ts.tv_nsec) / 1000000.0));
|
||||||
}
|
}
|
||||||
runshfunc(prog, w, name);
|
runshfunc(prog, w, name);
|
||||||
if (active) {
|
if (active) {
|
||||||
if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) {
|
if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) {
|
||||||
tv.tv_sec = tv.tv_usec = 0;
|
ts.tv_sec = ts.tv_nsec = 0;
|
||||||
gettimeofday(&tv, &dummy);
|
zgettime_monotonic_if_available(&ts);
|
||||||
|
|
||||||
now = ((((double) tv.tv_sec) * 1000.0) +
|
now = ((((double) ts.tv_sec) * 1000.0) +
|
||||||
(((double) tv.tv_usec) / 1000.0));
|
(((double) ts.tv_nsec) / 1000000.0));
|
||||||
f->self += now - sf.beg;
|
f->self += now - sf.beg;
|
||||||
for (sp = sf.prev; sp && sp->p != f; sp = sp->prev);
|
for (sp = sf.prev; sp && sp->p != f; sp = sp->prev);
|
||||||
if (!sp)
|
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)
|
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
|
||||||
struct timespec dts;
|
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) {
|
if (clock_gettime(CLOCK_MONOTONIC, &dts) < 0) {
|
||||||
|
#endif
|
||||||
zwarn("unable to retrieve CLOCK_MONOTONIC time: %e", errno);
|
zwarn("unable to retrieve CLOCK_MONOTONIC time: %e", errno);
|
||||||
ret--;
|
ret--;
|
||||||
} else {
|
} else {
|
||||||
|
|
21
Src/exec.c
21
Src/exec.c
|
@ -348,10 +348,9 @@ setlimits(char *nam)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
static pid_t
|
static pid_t
|
||||||
zfork(struct timeval *tv)
|
zfork(struct timespec *ts)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
struct timezone dummy_tz;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Is anybody willing to explain this test?
|
* Is anybody willing to explain this test?
|
||||||
|
@ -360,8 +359,8 @@ zfork(struct timeval *tv)
|
||||||
zerr("job table full");
|
zerr("job table full");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (tv)
|
if (ts)
|
||||||
gettimeofday(tv, &dummy_tz);
|
zgettime_monotonic_if_available(ts);
|
||||||
/*
|
/*
|
||||||
* Queueing signals is necessary on Linux because fork()
|
* Queueing signals is necessary on Linux because fork()
|
||||||
* manipulates mutexes, leading to deadlock in memory
|
* manipulates mutexes, leading to deadlock in memory
|
||||||
|
@ -460,7 +459,7 @@ zfork(struct timeval *tv)
|
||||||
int list_pipe = 0, simple_pline = 0;
|
int list_pipe = 0, simple_pline = 0;
|
||||||
|
|
||||||
static pid_t list_pipe_pid;
|
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 nowait, pline_level = 0;
|
||||||
static int list_pipe_child = 0, list_pipe_job;
|
static int list_pipe_child = 0, list_pipe_job;
|
||||||
static char list_pipe_text[JOBTEXTSIZE];
|
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)))) {
|
(jobtab[list_pipe_job].stat & STAT_STOPPED)))) {
|
||||||
pid_t pid = 0;
|
pid_t pid = 0;
|
||||||
int synch[2];
|
int synch[2];
|
||||||
struct timeval bgtime;
|
struct timespec bgtime;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A pipeline with the shell handling the right
|
* A pipeline with the shell handling the right
|
||||||
|
@ -2284,7 +2283,7 @@ closemn(struct multio **mfds, int fd, int type)
|
||||||
char buf[TCBUFSIZE];
|
char buf[TCBUFSIZE];
|
||||||
int len, i;
|
int len, i;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
struct timeval bgtime;
|
struct timespec bgtime;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to block SIGCHLD in case the process
|
* 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;
|
pid_t pid;
|
||||||
int synch[2], flags;
|
int synch[2], flags;
|
||||||
struct entersubsh_ret esret;
|
struct entersubsh_ret esret;
|
||||||
struct timeval bgtime;
|
struct timespec bgtime;
|
||||||
|
|
||||||
child_block();
|
child_block();
|
||||||
esret.gleader = -1;
|
esret.gleader = -1;
|
||||||
|
@ -2947,7 +2946,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
* for the "time" keyword
|
* for the "time" keyword
|
||||||
*/
|
*/
|
||||||
child_times_t shti, chti;
|
child_times_t shti, chti;
|
||||||
struct timeval then;
|
struct timespec then;
|
||||||
if (how & Z_TIMED)
|
if (how & Z_TIMED)
|
||||||
shelltime(&shti, &chti, &then, 0);
|
shelltime(&shti, &chti, &then, 0);
|
||||||
|
|
||||||
|
@ -5060,7 +5059,7 @@ getproc(char *cmd, char **eptr)
|
||||||
int out = *cmd == Inang;
|
int out = *cmd == Inang;
|
||||||
char *pnam;
|
char *pnam;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
struct timeval bgtime;
|
struct timespec bgtime;
|
||||||
|
|
||||||
#ifndef PATH_DEV_FD
|
#ifndef PATH_DEV_FD
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -5149,7 +5148,7 @@ getpipe(char *cmd, int nullexec)
|
||||||
Eprog prog;
|
Eprog prog;
|
||||||
int pipes[2], out = *cmd == Inang;
|
int pipes[2], out = *cmd == Inang;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
struct timeval bgtime;
|
struct timespec bgtime;
|
||||||
char *ends;
|
char *ends;
|
||||||
|
|
||||||
if (!(prog = parsecmd(cmd, &ends)))
|
if (!(prog = parsecmd(cmd, &ends)))
|
||||||
|
|
|
@ -2891,9 +2891,9 @@ flockhistfile(char *fn, int keep_trying)
|
||||||
/*
|
/*
|
||||||
* Timeout is ten seconds.
|
* 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) {
|
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
|
* Randomise wait to minimise clashes with shells exiting at
|
||||||
* the same time.
|
* the same time.
|
||||||
|
@ -3137,7 +3137,7 @@ static int lockhistct;
|
||||||
static int
|
static int
|
||||||
checklocktime(char *lockfile, long *sleep_usp, time_t then)
|
checklocktime(char *lockfile, long *sleep_usp, time_t then)
|
||||||
{
|
{
|
||||||
time_t now = time(NULL);
|
time_t now = zmonotime(NULL);
|
||||||
|
|
||||||
if (now + 10 < then) {
|
if (now + 10 < then) {
|
||||||
/* File is more than 10 seconds in the future? */
|
/* File is more than 10 seconds in the future? */
|
||||||
|
|
|
@ -1022,7 +1022,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
|
||||||
#ifdef USE_GETPWUID
|
#ifdef USE_GETPWUID
|
||||||
struct passwd *pswd;
|
struct passwd *pswd;
|
||||||
#endif
|
#endif
|
||||||
struct timezone dummy_tz;
|
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int i, j;
|
int i, j;
|
||||||
#if defined(SITEFPATH_DIR) || defined(FPATH_DIR) || defined (ADDITIONAL_FPATH) || defined(FIXED_FPATH_DIR)
|
#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 = '^';
|
hatchar = '^';
|
||||||
termflags = TERM_UNKNOWN;
|
termflags = TERM_UNKNOWN;
|
||||||
curjob = prevjob = coprocin = coprocout = -1;
|
curjob = prevjob = coprocin = coprocout = -1;
|
||||||
gettimeofday(&shtimer, &dummy_tz); /* init $SECONDS */
|
zgettime_monotonic_if_available(&shtimer); /* init $SECONDS */
|
||||||
srand((unsigned int)(shtimer.tv_sec + shtimer.tv_usec)); /* seed $RANDOM */
|
srand((unsigned int)(shtimer.tv_sec + shtimer.tv_nsec)); /* seed $RANDOM */
|
||||||
|
|
||||||
/* Set default path */
|
/* Set default path */
|
||||||
path = (char **) zalloc(sizeof(*path) * 5);
|
path = (char **) zalloc(sizeof(*path) * 5);
|
||||||
|
@ -1297,7 +1296,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
breaks = loops = 0;
|
breaks = loops = 0;
|
||||||
lastmailcheck = time(NULL);
|
lastmailcheck = zmonotime(NULL);
|
||||||
locallevel = sourcelevel = 0;
|
locallevel = sourcelevel = 0;
|
||||||
sfcontext = SFC_NONE;
|
sfcontext = SFC_NONE;
|
||||||
trap_return = 0;
|
trap_return = 0;
|
||||||
|
|
78
Src/jobs.c
78
Src/jobs.c
|
@ -136,7 +136,7 @@ int numpipestats, pipestats[MAX_PIPESTATS];
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
static struct timeval *
|
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_sec = t2->tv_sec - t1->tv_sec;
|
||||||
dt->tv_usec = t2->tv_usec - t1->tv_usec;
|
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;
|
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 */
|
/* change job table entry from stopped to running */
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
|
@ -349,7 +364,6 @@ get_usage(void)
|
||||||
void
|
void
|
||||||
update_process(Process pn, int status)
|
update_process(Process pn, int status)
|
||||||
{
|
{
|
||||||
struct timezone dummy_tz;
|
|
||||||
#ifdef HAVE_GETRUSAGE
|
#ifdef HAVE_GETRUSAGE
|
||||||
struct timeval childs = child_usage.ru_stime;
|
struct timeval childs = child_usage.ru_stime;
|
||||||
struct timeval childu = child_usage.ru_utime;
|
struct timeval childu = child_usage.ru_utime;
|
||||||
|
@ -360,12 +374,12 @@ update_process(Process pn, int status)
|
||||||
|
|
||||||
/* get time-accounting info */
|
/* get time-accounting info */
|
||||||
get_usage();
|
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 */
|
pn->status = status; /* save the status returned by WAIT */
|
||||||
#ifdef HAVE_GETRUSAGE
|
#ifdef HAVE_GETRUSAGE
|
||||||
dtime(&pn->ti.ru_stime, &childs, &child_usage.ru_stime);
|
dtime_tv(&pn->ti.ru_stime, &childs, &child_usage.ru_stime);
|
||||||
dtime(&pn->ti.ru_utime, &childu, &child_usage.ru_utime);
|
dtime_tv(&pn->ti.ru_utime, &childu, &child_usage.ru_utime);
|
||||||
#else
|
#else
|
||||||
pn->ti.st = shtms.tms_cstime - childs; /* compute process system space time */
|
pn->ti.st = shtms.tms_cstime - childs; /* compute process system space time */
|
||||||
pn->ti.ut = shtms.tms_cutime - childu; /* compute process user space time */
|
pn->ti.ut = shtms.tms_cutime - childu; /* compute process user space time */
|
||||||
|
@ -753,7 +767,7 @@ printhhmmss(double secs)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
printtime(struct timeval *real, child_times_t *ti, char *desc)
|
printtime(struct timespec *real, child_times_t *ti, char *desc)
|
||||||
{
|
{
|
||||||
char *s;
|
char *s;
|
||||||
double elapsed_time, user_time, system_time;
|
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 */
|
/* 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
|
#ifdef HAVE_GETRUSAGE
|
||||||
user_time = ti->ru_utime.tv_sec + ti->ru_utime.tv_usec / 1000000.0;
|
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;
|
system_time = ti->ru_stime.tv_sec + ti->ru_stime.tv_usec / 1000000.0;
|
||||||
total_time = user_time + system_time;
|
total_time = user_time + system_time;
|
||||||
percent = 100.0 * total_time
|
percent = 100.0 * total_time
|
||||||
/ (real->tv_sec + real->tv_usec / 1000000.0);
|
/ (real->tv_sec + real->tv_nsec / 1000000000.0);
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
long clktck = get_clktck();
|
long clktck = get_clktck();
|
||||||
user_time = ti->ut / (double) clktck;
|
user_time = ti->ut / (double) clktck;
|
||||||
system_time = ti->st / (double) clktck;
|
system_time = ti->st / (double) clktck;
|
||||||
percent = 100.0 * (ti->ut + ti->st)
|
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
|
#endif
|
||||||
|
|
||||||
|
@ -844,6 +858,23 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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 '*':
|
case '*':
|
||||||
switch (*++s) {
|
switch (*++s) {
|
||||||
case 'E':
|
case 'E':
|
||||||
|
@ -991,12 +1022,12 @@ static void
|
||||||
dumptime(Job jn)
|
dumptime(Job jn)
|
||||||
{
|
{
|
||||||
Process pn;
|
Process pn;
|
||||||
struct timeval dtimeval;
|
struct timespec dtimespec;
|
||||||
|
|
||||||
if (!jn->procs)
|
if (!jn->procs)
|
||||||
return;
|
return;
|
||||||
for (pn = jn->procs; pn; pn = pn->next)
|
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);
|
pn->text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1506,7 +1537,7 @@ deletejob(Job jn, int disowning)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
void
|
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)
|
int gleader, int list_pipe_job_used)
|
||||||
{
|
{
|
||||||
Process pn, *pnlist;
|
Process pn, *pnlist;
|
||||||
|
@ -1894,16 +1925,15 @@ spawnjob(void)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
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 timespec dtimespec, now;
|
||||||
struct timeval dtimeval, now;
|
|
||||||
child_times_t ti;
|
child_times_t ti;
|
||||||
#ifndef HAVE_GETRUSAGE
|
#ifndef HAVE_GETRUSAGE
|
||||||
struct tms buf;
|
struct tms buf;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gettimeofday(&now, &dummy_tz);
|
zgettime_monotonic_if_available(&now);
|
||||||
|
|
||||||
#ifdef HAVE_GETRUSAGE
|
#ifdef HAVE_GETRUSAGE
|
||||||
getrusage(RUSAGE_SELF, &ti);
|
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 (shell) {
|
||||||
if (delta) {
|
if (delta) {
|
||||||
#ifdef HAVE_GETRUSAGE
|
#ifdef HAVE_GETRUSAGE
|
||||||
dtime(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime);
|
dtime_tv(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime);
|
||||||
dtime(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime);
|
dtime_tv(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime);
|
||||||
#else
|
#else
|
||||||
ti.ut -= shell->ut;
|
ti.ut -= shell->ut;
|
||||||
ti.st -= shell->st;
|
ti.st -= shell->st;
|
||||||
|
@ -1926,15 +1956,15 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d
|
||||||
*shell = ti;
|
*shell = ti;
|
||||||
}
|
}
|
||||||
if (delta)
|
if (delta)
|
||||||
dtime(&dtimeval, then, &now);
|
dtime_ts(&dtimespec, then, &now);
|
||||||
else {
|
else {
|
||||||
if (then)
|
if (then)
|
||||||
*then = now;
|
*then = now;
|
||||||
dtime(&dtimeval, &shtimer, &now);
|
dtime_ts(&dtimespec, &shtimer, &now);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!delta == !shell)
|
if (!delta == !shell)
|
||||||
printtime(&dtimeval, &ti, "shell");
|
printtime(&dtimespec, &ti, "shell");
|
||||||
|
|
||||||
#ifdef HAVE_GETRUSAGE
|
#ifdef HAVE_GETRUSAGE
|
||||||
getrusage(RUSAGE_CHILDREN, &ti);
|
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 (kids) {
|
||||||
if (delta) {
|
if (delta) {
|
||||||
#ifdef HAVE_GETRUSAGE
|
#ifdef HAVE_GETRUSAGE
|
||||||
dtime(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime);
|
dtime_tv(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime);
|
||||||
dtime(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime);
|
dtime_tv(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime);
|
||||||
#else
|
#else
|
||||||
ti.ut -= shell->ut;
|
ti.ut -= shell->ut;
|
||||||
ti.st -= shell->st;
|
ti.st -= shell->st;
|
||||||
|
@ -1955,7 +1985,7 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d
|
||||||
*kids = ti;
|
*kids = ti;
|
||||||
}
|
}
|
||||||
if (!delta == !kids)
|
if (!delta == !kids)
|
||||||
printtime(&dtimeval, &ti, "children");
|
printtime(&dtimespec, &ti, "children");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see if jobs need printing */
|
/* 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';
|
unsigned char keyboardhackchar = '\0';
|
||||||
|
|
||||||
/* $SECONDS = now.tv_sec - shtimer.tv_sec
|
/* $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) */
|
* (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 */
|
/* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */
|
||||||
|
|
||||||
|
@ -4496,13 +4496,12 @@ randomsetfn(UNUSED(Param pm), zlong v)
|
||||||
zlong
|
zlong
|
||||||
intsecondsgetfn(UNUSED(Param pm))
|
intsecondsgetfn(UNUSED(Param pm))
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timespec now;
|
||||||
struct timezone dummy_tz;
|
|
||||||
|
|
||||||
gettimeofday(&now, &dummy_tz);
|
zgettime_monotonic_if_available(&now);
|
||||||
|
|
||||||
return (zlong)(now.tv_sec - shtimer.tv_sec -
|
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' */
|
/* Function to set value of special parameter `SECONDS' */
|
||||||
|
@ -4511,48 +4510,47 @@ intsecondsgetfn(UNUSED(Param pm))
|
||||||
void
|
void
|
||||||
intsecondssetfn(UNUSED(Param pm), zlong x)
|
intsecondssetfn(UNUSED(Param pm), zlong x)
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timespec now;
|
||||||
struct timezone dummy_tz;
|
|
||||||
zlong diff;
|
zlong diff;
|
||||||
|
|
||||||
gettimeofday(&now, &dummy_tz);
|
zgettime_monotonic_if_available(&now);
|
||||||
|
|
||||||
diff = (zlong)now.tv_sec - x;
|
diff = (zlong)now.tv_sec - x;
|
||||||
shtimer.tv_sec = diff;
|
shtimer.tv_sec = diff;
|
||||||
if ((zlong)shtimer.tv_sec != diff)
|
if ((zlong)shtimer.tv_sec != diff)
|
||||||
zwarn("SECONDS truncated on assignment");
|
zwarn("SECONDS truncated on assignment");
|
||||||
shtimer.tv_usec = now.tv_usec;
|
shtimer.tv_nsec = now.tv_nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
double
|
double
|
||||||
floatsecondsgetfn(UNUSED(Param pm))
|
floatsecondsgetfn(UNUSED(Param pm))
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timespec now;
|
||||||
struct timezone dummy_tz;
|
|
||||||
|
|
||||||
gettimeofday(&now, &dummy_tz);
|
zgettime_monotonic_if_available(&now);
|
||||||
|
|
||||||
return (double)(now.tv_sec - shtimer.tv_sec) +
|
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
|
void
|
||||||
floatsecondssetfn(UNUSED(Param pm), double x)
|
floatsecondssetfn(UNUSED(Param pm), double x)
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timespec now;
|
||||||
struct timezone dummy_tz;
|
|
||||||
|
zgettime_monotonic_if_available(&now);
|
||||||
|
|
||||||
gettimeofday(&now, &dummy_tz);
|
|
||||||
shtimer.tv_sec = now.tv_sec - (zlong)x;
|
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
|
double
|
||||||
getrawseconds(void)
|
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)
|
setrawseconds(double x)
|
||||||
{
|
{
|
||||||
shtimer.tv_sec = (zlong)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;
|
test = 1;
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
if (time(NULL) - shtimer.tv_sec >= arg)
|
if (zmonotime(NULL) - shtimer.tv_sec >= arg)
|
||||||
test = 1;
|
test = 1;
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
|
|
|
@ -342,8 +342,7 @@ wait_for_processes(void)
|
||||||
zwarn("job can't be suspended");
|
zwarn("job can't be suspended");
|
||||||
} else {
|
} else {
|
||||||
#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE)
|
#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE)
|
||||||
struct timezone dummy_tz;
|
zgettime_monotonic_if_available(&pn->endtime);
|
||||||
gettimeofday(&pn->endtime, &dummy_tz);
|
|
||||||
#ifdef WIFCONTINUED
|
#ifdef WIFCONTINUED
|
||||||
if (WIFCONTINUED(status))
|
if (WIFCONTINUED(status))
|
||||||
pn->status = SP_RUNNING;
|
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 *
|
/* If 1) the parameter PERIOD exists, 2) a hook function for *
|
||||||
* "periodic" exists, 3) it's been greater than PERIOD since we *
|
* "periodic" exists, 3) it's been greater than PERIOD since we *
|
||||||
* executed any such hook, then execute it now. */
|
* 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))
|
!callhookfunc("periodic", NULL, 1, NULL))
|
||||||
lastperiodic = time(NULL);
|
lastperiodic = zmonotime(NULL);
|
||||||
if (errflag)
|
if (errflag)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Check mail */
|
/* Check mail */
|
||||||
currentmailcheck = time(NULL);
|
currentmailcheck = zmonotime(NULL);
|
||||||
if (mailcheck &&
|
if (mailcheck &&
|
||||||
(zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) {
|
(zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) {
|
||||||
char *mailfile;
|
char *mailfile;
|
||||||
|
@ -2761,6 +2761,19 @@ timespec_diff_us(const struct timespec *t1, const struct timespec *t2)
|
||||||
return (reverse ? LONG_MIN : LONG_MAX);
|
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
|
* Sleep for the given number of microseconds --- must be within
|
||||||
* range of a long at the moment, but this is only used for
|
* 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.
|
* 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.
|
* Return 1 if that seemed to work, else 0.
|
||||||
*
|
*
|
||||||
* For best results max_us should be a multiple of 2**16 or large
|
* 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)
|
zsleep_random(long max_us, time_t end_time)
|
||||||
{
|
{
|
||||||
long r;
|
long r;
|
||||||
time_t now = time(NULL);
|
time_t now = zmonotime(NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Randomish backoff. Doesn't need to be fundamentally
|
* Randomish backoff. Doesn't need to be fundamentally
|
||||||
|
|
|
@ -1115,8 +1115,8 @@ struct process {
|
||||||
char text[JOBTEXTSIZE]; /* text to print when 'jobs' is run */
|
char text[JOBTEXTSIZE]; /* text to print when 'jobs' is run */
|
||||||
int status; /* return code from waitpid/wait3() */
|
int status; /* return code from waitpid/wait3() */
|
||||||
child_times_t ti;
|
child_times_t ti;
|
||||||
struct timeval bgtime; /* time job was spawned */
|
struct timespec bgtime; /* time job was spawned */
|
||||||
struct timeval endtime; /* time job exited */
|
struct timespec endtime; /* time job exited */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct execstack {
|
struct execstack {
|
||||||
|
|
|
@ -11,9 +11,44 @@
|
||||||
(time cat) >&/dev/null
|
(time cat) >&/dev/null
|
||||||
0:`time' keyword (status only)
|
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:`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
|
time x=1
|
||||||
0:`time' simple assignment
|
0:`time' simple assignment
|
||||||
|
|
|
@ -68,6 +68,13 @@
|
||||||
print -P '%(?.true.false)'
|
print -P '%(?.true.false)'
|
||||||
0:ternary prompt escapes
|
0:ternary prompt escapes
|
||||||
>true
|
>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
|
>false
|
||||||
|
|
||||||
print -P 'start %10<...<truncated at 10%<< Not truncated%3< ...<Not shown'
|
print -P 'start %10<...<truncated at 10%<< Not truncated%3< ...<Not shown'
|
||||||
|
|
|
@ -1550,6 +1550,28 @@
|
||||||
>1
|
>1
|
||||||
>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=("|" "?")
|
foo=("|" "?")
|
||||||
[[ "|" = ${(j.|.)foo} ]] && print yes || print no
|
[[ "|" = ${(j.|.)foo} ]] && print yes || print no
|
||||||
[[ "|" = ${(j.|.)~foo} ]] && print yes || print no
|
[[ "|" = ${(j.|.)~foo} ]] && print yes || print no
|
||||||
|
|
Loading…
Reference in a new issue