Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys New cv_timedwaitclock, cv_timedwaitclock_sig.
details: https://anonhg.NetBSD.org/src/rev/98d52f2a4a21
branches: trunk
changeset: 971740:98d52f2a4a21
user: riastradh <riastradh%NetBSD.org@localhost>
date: Sun May 03 01:24:37 2020 +0000
description:
New cv_timedwaitclock, cv_timedwaitclock_sig.
Usage: given a struct timespec timeout copied from userland, along
with a clockid and TIMER_* flags,
error = cv_timedwaitclock(cv, lock, timeout, clockid, flags,
DEFAULT_TIMEOUT_EPSILON);
if (error)
/* fail */
If flags is relative (i.e., (flags & TIMER_ABSTIME) == 0), then this
deducts the time spent waiting from timeout, so you can run it in a
loop:
struct timespec timeout;
error = copyin(SCARG(uap, timeout), &timeout, sizeof timeout);
if (error)
return error;
mutex_enter(lock);
while (!ready()) {
error = cv_timedwaitclock_sig(cv, lock, &timeout,
SCARG(uap, clockid), SCARG(uap, flags),
DEFAULT_TIMEOUT_EPSILON);
if (error)
break;
}
mutex_exit(lock);
CAVEAT: If the system call is interrupted by a signal with SA_RESTART
so cv_timedwaitclock_sig fails with ERESTART, then the system call
will be restarted with the _original_ relative timeout, not counting
the time that was already spent waiting. This is a problem but it's
not a problem I want to deal with at the moment.
diffstat:
sys/kern/kern_condvar.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++-
sys/sys/condvar.h | 7 +-
2 files changed, 191 insertions(+), 3 deletions(-)
diffs (236 lines):
diff -r 22a968040bbb -r 98d52f2a4a21 sys/kern/kern_condvar.c
--- a/sys/kern/kern_condvar.c Sun May 03 01:20:37 2020 +0000
+++ b/sys/kern/kern_condvar.c Sun May 03 01:24:37 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_condvar.c,v 1.48 2020/05/03 01:19:47 riastradh Exp $ */
+/* $NetBSD: kern_condvar.c,v 1.49 2020/05/03 01:24:37 riastradh Exp $ */
/*-
* Copyright (c) 2006, 2007, 2008, 2019, 2020 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_condvar.c,v 1.48 2020/05/03 01:19:47 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_condvar.c,v 1.49 2020/05/03 01:24:37 riastradh Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -244,6 +244,189 @@
return error;
}
+struct timedwaitclock {
+ struct timespec *timeout;
+ clockid_t clockid;
+ int flags;
+ const struct bintime *epsilon;
+ struct timespec starttime;
+};
+
+static int
+cv_timedwaitclock_begin(struct timedwaitclock *T, int *timo)
+{
+ struct timespec delta;
+ const struct timespec *deltap;
+ int error;
+
+ /* Sanity-check timeout -- may have come from userland. */
+ if (T->timeout->tv_nsec < 0 || T->timeout->tv_nsec >= 1000000000L)
+ return EINVAL;
+
+ /*
+ * Compute the time delta.
+ */
+ if ((T->flags & TIMER_ABSTIME) == TIMER_ABSTIME) {
+ /* Check our watch. */
+ error = clock_gettime1(T->clockid, &T->starttime);
+ if (error)
+ return error;
+
+ /* If the deadline has passed, we're done. */
+ if (timespeccmp(T->timeout, &T->starttime, <=))
+ return ETIMEDOUT;
+
+ /* Count how much time is left. */
+ timespecsub(T->timeout, &T->starttime, &delta);
+ deltap = δ
+ } else {
+ /* The user specified how much time is left. */
+ deltap = T->timeout;
+
+ /* If there's none left, we've timed out. */
+ if (deltap->tv_sec == 0 && deltap->tv_nsec == 0)
+ return ETIMEDOUT;
+ }
+
+ /*
+ * Convert to ticks, but clamp to be >=1.
+ *
+ * XXX In the tickless future, use a high-resolution timer if
+ * timo would round to zero.
+ */
+ *timo = tstohz(deltap);
+ KASSERTMSG(*timo >= 0, "negative ticks: %d", *timo);
+ if (*timo == 0)
+ *timo = 1;
+
+ /* Success! */
+ return 0;
+}
+
+static void
+cv_timedwaitclock_end(struct timedwaitclock *T)
+{
+ struct timespec endtime, delta;
+
+ /* If the timeout is absolute, nothing to do. */
+ if ((T->flags & TIMER_ABSTIME) == TIMER_ABSTIME)
+ return;
+
+ /*
+ * Check our watch. If anything goes wrong with it, make sure
+ * that the next time we immediately time out rather than fail
+ * to deduct the time elapsed.
+ */
+ if (clock_gettime1(T->clockid, &endtime)) {
+ T->timeout->tv_sec = 0;
+ T->timeout->tv_nsec = 0;
+ return;
+ }
+
+ /* Find how much time elapsed while we waited. */
+ timespecsub(&endtime, &T->starttime, &delta);
+
+ /*
+ * Paranoia: If the clock went backwards, treat it as if no
+ * time elapsed at all rather than adding anything.
+ */
+ if (delta.tv_sec < 0 ||
+ (delta.tv_sec == 0 && delta.tv_nsec < 0)) {
+ delta.tv_sec = 0;
+ delta.tv_nsec = 0;
+ }
+
+ /*
+ * Set it to the time left, or zero, whichever is larger. We
+ * do not fail with EWOULDBLOCK here because this may have been
+ * an explicit wakeup, so the caller needs to check before they
+ * give up or else cv_signal would be lost.
+ */
+ if (timespeccmp(T->timeout, &delta, <=)) {
+ T->timeout->tv_sec = 0;
+ T->timeout->tv_nsec = 0;
+ } else {
+ timespecsub(T->timeout, &delta, T->timeout);
+ }
+}
+
+/*
+ * cv_timedwaitclock:
+ *
+ * Wait on a condition variable until awoken normally, or the
+ * specified timeout expires according to the provided clock.
+ * Returns zero if awoken normally or EWOULDBLOCK if the timeout
+ * expired. For relative timeouts ((flags & TIMER_ABSTIME) == 0),
+ * updates timeout with the time left.
+ *
+ * timeout == NULL specifies an infinite timeout. epsilon is a
+ * requested maximum error in timeout (excluding spurious
+ * wakeups).
+ */
+int
+cv_timedwaitclock(kcondvar_t *cv, kmutex_t *mtx, struct timespec *timeout,
+ clockid_t clockid, int flags, const struct bintime *epsilon)
+{
+ struct timedwaitclock T = {
+ .timeout = timeout,
+ .clockid = clockid,
+ .flags = flags,
+ .epsilon = epsilon,
+ };
+ int timo;
+ int error;
+
+ if (timeout == NULL) {
+ cv_wait(cv, mtx);
+ return 0;
+ }
+
+ error = cv_timedwaitclock_begin(&T, &timo);
+ if (error)
+ return error;
+ error = cv_timedwait(cv, mtx, timo);
+ cv_timedwaitclock_end(&T);
+ return error;
+}
+
+/*
+ * cv_timedwaitclock_sig:
+ *
+ * Wait on a condition variable until awoken normally, interrupted
+ * by a signal, or the specified timeout expires according to the
+ * provided clock. Returns zero if awoken normally,
+ * EINTR/ERESTART if interrupted by a signal, or EWOULDBLOCK if
+ * the timeout expired. For relative timeouts ((flags &
+ * TIMER_ABSTIME) == 0), updates timeout with the time left.
+ *
+ * timeout == NULL specifies an infinite timeout. epsilon is a
+ * requested maximum error in timeout (excluding spurious
+ * wakeups).
+ */
+int
+cv_timedwaitclock_sig(kcondvar_t *cv, kmutex_t *mtx, struct timespec *timeout,
+ clockid_t clockid, int flags, const struct bintime *epsilon)
+{
+ struct timedwaitclock T = {
+ .timeout = timeout,
+ .clockid = clockid,
+ .flags = flags,
+ .epsilon = epsilon,
+ };
+ int timo;
+ int error;
+
+ if (timeout == NULL)
+ return cv_wait_sig(cv, mtx);
+
+ error = cv_timedwaitclock_begin(&T, &timo);
+ if (error)
+ return error;
+ error = cv_timedwait_sig(cv, mtx, timo);
+ cv_timedwaitclock_end(&T);
+ return error;
+}
+
/*
* Given a number of seconds, sec, and 2^64ths of a second, frac, we
* want a number of ticks for a timeout:
diff -r 22a968040bbb -r 98d52f2a4a21 sys/sys/condvar.h
--- a/sys/sys/condvar.h Sun May 03 01:20:37 2020 +0000
+++ b/sys/sys/condvar.h Sun May 03 01:24:37 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: condvar.h,v 1.15 2020/03/26 19:46:42 ad Exp $ */
+/* $NetBSD: condvar.h,v 1.16 2020/05/03 01:24:37 riastradh Exp $ */
/*-
* Copyright (c) 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc.
@@ -40,6 +40,7 @@
struct bintime;
struct kmutex;
+struct timespec;
void cv_init(kcondvar_t *, const char *);
void cv_destroy(kcondvar_t *);
@@ -48,6 +49,10 @@
int cv_wait_sig(kcondvar_t *, struct kmutex *);
int cv_timedwait(kcondvar_t *, struct kmutex *, int);
int cv_timedwait_sig(kcondvar_t *, struct kmutex *, int);
+int cv_timedwaitclock(kcondvar_t *, struct kmutex *, struct timespec *,
+ clockid_t, int, const struct bintime *);
+int cv_timedwaitclock_sig(kcondvar_t *, struct kmutex *, struct timespec *,
+ clockid_t, int, const struct bintime *);
int cv_timedwaitbt(kcondvar_t *, struct kmutex *, struct bintime *,
const struct bintime *);
int cv_timedwaitbt_sig(kcondvar_t *, struct kmutex *, struct bintime *,
Home |
Main Index |
Thread Index |
Old Index