summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--Doc/Zsh/params.yo18
-rw-r--r--Doc/Zsh/prompt.yo3
-rw-r--r--Src/Modules/zftp.c4
-rw-r--r--Src/Modules/zprof.c19
-rw-r--r--Src/compat.c12
-rw-r--r--Src/exec.c21
-rw-r--r--Src/hist.c6
-rw-r--r--Src/init.c7
-rw-r--r--Src/jobs.c78
-rw-r--r--Src/params.c38
-rw-r--r--Src/prompt.c2
-rw-r--r--Src/signals.c3
-rw-r--r--Src/utils.c25
-rw-r--r--Src/zsh.h4
-rw-r--r--Test/A08time.ztst39
-rw-r--r--Test/D01prompt.ztst7
-rw-r--r--Test/D04parameter.ztst22
18 files changed, 225 insertions, 92 deletions
diff --git a/ChangeLog b/ChangeLog
index a58002d71..989cc0aa3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 02ce796a9..69298855f 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -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))(
diff --git a/Doc/Zsh/prompt.yo b/Doc/Zsh/prompt.yo
index de988ab7c..108cb62e5 100644
--- a/Doc/Zsh/prompt.yo
+++ b/Doc/Zsh/prompt.yo
@@ -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.)
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index b60e5bf31..230ad86f6 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -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);
diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c
index 171a15b90..f5a50effc 100644
--- a/Src/Modules/zprof.c
+++ b/Src/Modules/zprof.c
@@ -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)
diff --git a/Src/compat.c b/Src/compat.c
index 8b31ad9f4..918d98e69 100644
--- a/Src/compat.c
+++ b/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 {
diff --git a/Src/exec.c b/Src/exec.c
index bc07e8c39..874ff41f7 100644
--- a/Src/exec.c
+++ b/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)))
diff --git a/Src/hist.c b/Src/hist.c
index 1a00c30ed..d0960a284 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -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? */
diff --git a/Src/init.c b/Src/init.c
index 61f759ded..378aee348 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -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;
diff --git a/Src/jobs.c b/Src/jobs.c
index 39c664388..ad14f6312 100644
--- a/Src/jobs.c
+++ b/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 */
diff --git a/Src/params.c b/Src/params.c
index 6f137585b..d1c06b893 100644
--- a/Src/params.c
+++ b/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);
}
/**/
diff --git a/Src/prompt.c b/Src/prompt.c
index e10b05215..f36aba9d3 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -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':
diff --git a/Src/signals.c b/Src/signals.c
index 86f1a49f6..de42f302d 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -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;
diff --git a/Src/utils.c b/Src/utils.c
index ce4e875fd..5c91dfcda 100644
--- a/Src/utils.c
+++ b/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
diff --git a/Src/zsh.h b/Src/zsh.h
index 090abf8f5..85b5c9bdc 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -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 {
diff --git a/Test/A08time.ztst b/Test/A08time.ztst
index 22a460f5e..4a41cc76a 100644
--- a/Test/A08time.ztst
+++ b/Test/A08time.ztst
@@ -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
diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst
index 55861cca1..f42e19714 100644
--- a/Test/D01prompt.ztst
+++ b/Test/D01prompt.ztst
@@ -70,6 +70,13 @@
>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'
print -P 'start %10>...>truncated at 10%>> Not truncated%3> ...>Not shown'
0:prompt truncation
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index 7953827d6..ed25fd7a9 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -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