Page MenuHomeFreeBSD

D37102.diff
No OneTemporary

D37102.diff

diff --git a/lib/libc/sys/kqueue.2 b/lib/libc/sys/kqueue.2
--- a/lib/libc/sys/kqueue.2
+++ b/lib/libc/sys/kqueue.2
@@ -145,6 +145,15 @@
and
.Fa eventlist .
.Pp
+The special value
+.Va KQUEUE_FD_ANON
+may be given as the
+.Va fd
+argument to
+.Fn kevent .
+In this case, an anonymous temporary queue is used, and no longer exists
+after the call returns.
+.Pp
The
.Fn EV_SET
macro is provided for ease of initializing a
@@ -262,6 +271,9 @@
See
.Sx RETURN VALUES
below.
+.It Dv EV_ANON
+This is used for anonymous operations that are not associated with a particular
+kqueue descriptor.
.It Dv EV_KEEPUDATA
Causes
.Fn kevent
@@ -662,6 +674,46 @@
On return,
.Va fflags
contains the users defined flags in the lower 24 bits.
+.It Dv EVFILT_USERMEM
+Waits for a value to change at an address in user memory. The address
+is provided in
+.Va ident .
+The value it points to is compared with an expected value provided in
+.Va data ,
+and the event is triggered immediately if they are not equal. Otherwise, it
+must be triggered explicitly using
+.Va NOTE_TRIGGER
+by any thread that wants to advertise that it has changed the value. The event
+can be triggered by any process that has the same memory object mapped, even at
+a different address, unless
+.Va NOTE_USERMEM_PRIVATE
+is added to
+.Va fflags
+to prevent inter-process events.
+When
+.Va NOTE_TRIGGER
+is used in
+.Va fflags ,
+.Va flags
+should be set to
+.Va EV_ANON ,
+because this operation wakes waiters not only in the given kqueue object.
+.Bl -tag -width "Dv NOTE_UMTX_WAIT_PRIVATE"
+.It Dv NOTE_USERMEM_INT
+.Va ident
+points to an object the size of an integer.
+.It Dv NOTE_USERMEM_LONG
+.Va ident
+points to an object the size of a long.
+.It Dv NOTE_TRIGGER
+Cause the event to be triggered. The number of waiters to wake up should
+be provided in
+.Va data .
+.It Dv NOTE_USERMEM_PRIVATE
+.Va ident
+is in a private namespace that is not shared with other processes that map
+the containing memory object. Can be ORed with the preceding values.
+.El
.El
.Sh CANCELLATION BEHAVIOUR
If
diff --git a/share/man/man9/kqueue.9 b/share/man/man9/kqueue.9
--- a/share/man/man9/kqueue.9
+++ b/share/man/man9/kqueue.9
@@ -212,6 +212,14 @@
that the
.Va knote
was added to.
+.It Va f_anon
+The
+.Va f_anon
+function will be called for
+.Vt kevent
+objects with the
+.Dv EV_ANON
+flag, to perform actions that don't relate to a specific kqueue or knote.
.El
.Pp
The function
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -70,6 +70,7 @@
#include <sys/syscallsubr.h>
#include <sys/taskqueue.h>
#include <sys/uio.h>
+#include <sys/umtx.h>
#include <sys/user.h>
#ifdef KTRACE
#include <sys/ktrace.h>
@@ -203,6 +204,13 @@
.f_event = filt_user,
.f_touch = filt_usertouch,
};
+static struct filterops usermem_filtops = {
+ .f_attach = filt_usermemattach,
+ .f_detach = filt_usermemdetach,
+ .f_event = filt_usermem,
+ .f_touch = filt_usermemtouch,
+ .f_anon = filt_usermemanon,
+};
static uma_zone_t knote_zone;
static unsigned int __exclusive_cache_line kq_ncallouts;
@@ -361,6 +369,7 @@
{ &user_filtops, 1 }, /* EVFILT_USER */
{ &null_filtops }, /* EVFILT_SENDFILE */
{ &file_filtops, 1 }, /* EVFILT_EMPTY */
+ { &usermem_filtops, 1 }, /* EVFILT_USERMEM */
};
/*
@@ -1293,6 +1302,11 @@
struct file *fp;
int error;
+ if (fd == KQUEUE_FD_ANON) {
+ error = kern_kevent_anonymous(td, nchanges, nevents, k_ops, timeout);
+ return (error);
+ }
+
cap_rights_init_zero(&rights);
if (nchanges > 0)
cap_rights_set_one(&rights, CAP_KQUEUE_CHANGE);
@@ -1372,15 +1386,15 @@
* used to perform one-shot polling, similar to poll() and select().
*/
int
-kern_kevent_anonymous(struct thread *td, int nevents,
- struct kevent_copyops *k_ops)
+kern_kevent_anonymous(struct thread *td, int nchanges, int nevents,
+ struct kevent_copyops *k_ops, const struct timespec *timeout)
{
struct kqueue kq = {};
int error;
kqueue_init(&kq);
kq.kq_refcnt = 1;
- error = kqueue_kevent(&kq, td, nevents, nevents, k_ops, NULL);
+ error = kqueue_kevent(&kq, td, nchanges, nevents, k_ops, timeout);
kqueue_drain(&kq, td);
kqueue_destroy(&kq);
return (error);
@@ -1500,6 +1514,17 @@
if (fops == NULL)
return EINVAL;
+ /* Anonymous actions have no kqueue or knote */
+ if (kev->flags & EV_ANON) {
+ tkn = NULL;
+ if (fops->f_anon == NULL) {
+ error = EINVAL;
+ goto done;
+ }
+ error = fops->f_anon(kev);
+ goto done;
+ }
+
if (kev->flags & EV_ADD) {
/* Reject an invalid flag pair early */
if (kev->flags & EV_KEEPUDATA) {
diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c
--- a/sys/kern/kern_umtx.c
+++ b/sys/kern/kern_umtx.c
@@ -60,6 +60,7 @@
#include <sys/syscallsubr.h>
#include <sys/taskqueue.h>
#include <sys/time.h>
+#include <sys/event.h>
#include <sys/eventhandler.h>
#include <sys/umtx.h>
#include <sys/umtxvar.h>
@@ -598,16 +599,22 @@
umtxq_signal_queue(struct umtx_key *key, int n_wake, int q)
{
struct umtxq_queue *uh;
- struct umtx_q *uq;
+ struct umtx_q *uq, *uq_temp;
int ret;
ret = 0;
UMTXQ_LOCKED_ASSERT(umtxq_getchain(key));
uh = umtxq_queue_lookup(key, q);
if (uh != NULL) {
- while ((uq = TAILQ_FIRST(&uh->head)) != NULL) {
- umtxq_remove_queue(uq, q);
- wakeup(uq);
+ TAILQ_FOREACH_SAFE(uq, &uh->head, uq_link, uq_temp) {
+ if (uq->uq_flags & UQF_KQUEUE) {
+ if (uq->uq_flags & UQF_KQUEUE_ONCE)
+ umtxq_remove_queue(uq, q);
+ KNOTE_LOCKED(&uq->uq_klist, 1);
+ } else {
+ umtxq_remove_queue(uq, q);
+ wakeup(uq);
+ }
if (++ret >= n_wake)
return (ret);
}
@@ -5124,3 +5131,166 @@
if (rb_inact != 0)
(void)umtx_handle_rb(td, rb_inact, NULL, true, compat32);
}
+
+int
+filt_usermemattach(struct knote *kn)
+{
+ struct umtx_q *uq;
+ void *uaddr;
+ u_long tmp;
+ u_int tmp32;
+ int error;
+ bool is_private;
+
+ is_private = (kn->kn_sfflags & NOTE_USERMEM_PRIVATE) != 0;
+
+ uaddr = (void *)kn->kn_kevent.ident;
+
+ /* XXX stricter checking of acceptable fflags */
+ if ((kn->kn_sfflags & NOTE_USERMEM_INT) == 0 &&
+ (kn->kn_sfflags & NOTE_USERMEM_LONG) == 0)
+ return (EINVAL);
+
+ /* Make a new umtx queue object and join the wait list. */
+ uq = umtxq_alloc();
+ if ((error = umtx_key_get(uaddr, TYPE_SIMPLE_WAIT,
+ is_private ? THREAD_SHARE : AUTO_SHARE, &uq->uq_key)) != 0) {
+ goto exit_free;
+ }
+
+ knlist_init_mtx(&uq->uq_klist, &umtxq_getchain(&uq->uq_key)->uc_lock);
+ uq->uq_flags |= UQF_KQUEUE;
+ if (kn->kn_flags & EV_ONESHOT)
+ uq->uq_flags |= UQF_KQUEUE_ONCE;
+ kn->kn_ptr.p_v = uq;
+
+ umtxq_lock(&uq->uq_key);
+ knlist_add(&uq->uq_klist, kn, 1);
+ umtxq_insert(uq);
+ umtxq_unlock(&uq->uq_key);
+
+ /* Fetch the user's value. */
+ if (kn->kn_sfflags & NOTE_USERMEM_INT) {
+ /* XXX also need to use this for _LONG for 32 bit process? */
+ error = fueword32(uaddr, &tmp32);
+ tmp = tmp32;
+ } else {
+ error = fueword(uaddr, &tmp);
+ }
+ if (error != 0) {
+ error = EFAULT;
+ goto exit_dequeue;
+ }
+
+ /* If value doesn't match, trigger immediately. */
+ if (tmp != (u_long) kn->kn_sdata) {
+ kn->kn_hookid = 1;
+ if (uq->uq_flags & UQF_KQUEUE_ONCE) {
+ umtxq_lock(&uq->uq_key);
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+ }
+ }
+
+ /* Otherwise we're now in the queue, waiting. */
+ return (0);
+
+exit_dequeue:
+ umtxq_lock(&uq->uq_key);
+ knlist_remove(&uq->uq_klist, kn, 1);
+ kn->kn_ptr.p_v = NULL;
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+
+ umtx_key_release(&uq->uq_key);
+
+exit_free:
+ umtxq_free(uq);
+
+ return (error);
+}
+
+void
+filt_usermemdetach(struct knote *kn)
+{
+ struct umtx_q *uq;
+
+ uq = kn->kn_ptr.p_v;
+
+ umtxq_lock(&uq->uq_key);
+ knlist_remove(&uq->uq_klist, kn, 1);
+ kn->kn_ptr.p_v = NULL;
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+
+ umtx_key_release(&uq->uq_key);
+
+ umtxq_free(uq);
+}
+
+int
+filt_usermem(struct knote *kn, long hint)
+{
+ if (hint)
+ kn->kn_hookid = 1;
+
+ return (kn->kn_hookid);
+}
+
+void
+filt_usermemtouch(struct knote *kn, struct kevent *kev, u_long type)
+{
+ switch (type) {
+ case EVENT_REGISTER:
+ /*
+ * Re-adding the same ident.
+ *
+ * XXX If data and fflags haven't changed, there's nothing to
+ * do as we're already in the umtx queue and anyone who changed
+ * the value should have woken us. Otherwise, we'd need to
+ * compare the value, but we can't here: we hold a
+ * non-sleepable lock, but reading userspace memory acquires a
+ * sleepable lock. So in that case, trigger immediately and
+ * let the user deal with it.
+ */
+ if (kev->data != kn->kn_sdata || kev->fflags != kn->kn_sfflags) {
+ struct umtx_q *uq;
+
+ kn->kn_hookid = 1;
+ kn->kn_sdata = kev->data;
+ kn->kn_fflags = kn->kn_sfflags;
+
+ uq = kn->kn_ptr.p_v;
+ if (uq->uq_flags & UQF_KQUEUE_ONCE)
+ umtxq_remove(uq);
+ }
+ break;
+ case EVENT_PROCESS:
+ *kev = kn->kn_kevent;
+ kn->kn_hookid = 0;
+ break;
+ default:
+ panic("filt_usermemtouch() - invalid type (%ld)", type);
+ break;
+ }
+}
+
+int
+filt_usermemanon(struct kevent *kev)
+{
+ int is_private;
+ void *uaddr;
+ int n_wake;
+ int error;
+
+ if (kev->fflags != NOTE_TRIGGER)
+ return (EINVAL);
+
+ is_private = (kev->fflags & NOTE_USERMEM_PRIVATE) != 0;
+ uaddr = (void *)kev->ident;
+ n_wake = kev->data;
+
+ error = kern_umtx_wake(curthread, uaddr, n_wake, is_private);
+
+ return (error);
+}
diff --git a/sys/sys/event.h b/sys/sys/event.h
--- a/sys/sys/event.h
+++ b/sys/sys/event.h
@@ -34,6 +34,8 @@
#include <sys/_types.h>
#include <sys/queue.h>
+#define KQUEUE_FD_ANON (-2) /* anonymous temporary kqueue */
+
#define EVFILT_READ (-1)
#define EVFILT_WRITE (-2)
#define EVFILT_AIO (-3) /* attached to aio requests */
@@ -47,7 +49,8 @@
#define EVFILT_USER (-11) /* User events */
#define EVFILT_SENDFILE (-12) /* attached to sendfile requests */
#define EVFILT_EMPTY (-13) /* empty send socket buf */
-#define EVFILT_SYSCOUNT 13
+#define EVFILT_USERMEM (-14) /* attached to value in user memory */
+#define EVFILT_SYSCOUNT 14
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define EV_SET(kevp_, a, b, c, d, e, f) do { \
@@ -145,6 +148,7 @@
#define EV_CLEAR 0x0020 /* clear event state after reporting */
#define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */
#define EV_DISPATCH 0x0080 /* disable event after reporting */
+#define EV_ANON 0x0400 /* anonymous action (not specific kq) */
#define EV_SYSFLAGS 0xF000 /* reserved by system */
#define EV_DROP 0x1000 /* note should be dropped */
@@ -218,6 +222,11 @@
#define NOTE_NSECONDS 0x00000008 /* data is nanoseconds */
#define NOTE_ABSTIME 0x00000010 /* timeout is absolute */
+/* addition flags for EVFILT_USERMEM */
+#define NOTE_USERMEM_INT 0x00000001 /* ident is pointer to int */
+#define NOTE_USERMEM_LONG 0x00000002 /* ident is pointer to long */
+#define NOTE_USERMEM_PRIVATE 0x00000004 /* no inter-process events */
+
struct knote;
SLIST_HEAD(klist, knote);
struct kqueue;
@@ -267,6 +276,7 @@
void (*f_detach)(struct knote *kn);
int (*f_event)(struct knote *kn, long hint);
void (*f_touch)(struct knote *kn, struct kevent *kev, u_long type);
+ int (*f_anon)(struct kevent *kev);
};
/*
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -185,8 +185,8 @@
int kern_jail_set(struct thread *td, struct uio *options, int flags);
int kern_kevent(struct thread *td, int fd, int nchanges, int nevents,
struct kevent_copyops *k_ops, const struct timespec *timeout);
-int kern_kevent_anonymous(struct thread *td, int nevents,
- struct kevent_copyops *k_ops);
+int kern_kevent_anonymous(struct thread *td, int nchanges, int nevents,
+ struct kevent_copyops *k_ops, const struct timespec *timeout);
int kern_kevent_fp(struct thread *td, struct file *fp, int nchanges,
int nevents, struct kevent_copyops *k_ops,
const struct timespec *timeout);
diff --git a/sys/sys/umtx.h b/sys/sys/umtx.h
--- a/sys/sys/umtx.h
+++ b/sys/sys/umtx.h
@@ -138,4 +138,13 @@
__END_DECLS
+#ifdef _KERNEL
+struct knote;
+int filt_usermemattach(struct knote *kn);
+void filt_usermemdetach(struct knote *kn);
+int filt_usermem(struct knote *kn, long hint);
+void filt_usermemtouch(struct knote *kn, struct kevent *touch, u_long type);
+int filt_usermemanon(struct kevent *kev);
+#endif
+
#endif /* !_SYS_UMTX_H_ */
diff --git a/sys/sys/umtxvar.h b/sys/sys/umtxvar.h
--- a/sys/sys/umtxvar.h
+++ b/sys/sys/umtxvar.h
@@ -120,6 +120,8 @@
/* Umtx flags. */
int uq_flags;
#define UQF_UMTXQ 0x0001
+#define UQF_KQUEUE 0x0002
+#define UQF_KQUEUE_ONCE 0x0004
/* Futex bitset mask */
u_int uq_bitset;
@@ -148,6 +150,9 @@
/* The queue we on */
struct umtxq_queue *uq_cur_queue;
+
+ /* List to hold knote, if waiting with kevent. */
+ struct knlist uq_klist;
};
TAILQ_HEAD(umtxq_head, umtx_q);
diff --git a/tests/sys/kqueue/Makefile b/tests/sys/kqueue/Makefile
--- a/tests/sys/kqueue/Makefile
+++ b/tests/sys/kqueue/Makefile
@@ -6,6 +6,7 @@
BINDIR= ${TESTSDIR}
ATF_TESTS_C+= kqueue_peek_signal
+ATF_TESTS_C+= kqueue_usermem
NETBSD_ATF_TESTS_C= proc1_test
# XXX: fails `ke.fflags & NOTE_TRACKERR` invariant
diff --git a/tests/sys/kqueue/kqueue_usermem.c b/tests/sys/kqueue/kqueue_usermem.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/kqueue/kqueue_usermem.c
@@ -0,0 +1,320 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+#include <sys/event.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/umtx.h> /* only needed to test _umtx_op iterop */
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+
+ATF_TC_WITHOUT_HEAD(main);
+
+ATF_TC_BODY(main, tc)
+{
+ struct timespec zero_timeout = {0, 0};
+ u_long *shmem;
+ int shmem_fd;
+ int rv;
+
+ shmem_fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0);
+ ATF_REQUIRE(shmem_fd >= 0);
+
+ rv = ftruncate(shmem_fd, sizeof(*shmem));
+ ATF_REQUIRE(rv == 0);
+
+ shmem = mmap(NULL, sizeof(*shmem), PROT_READ | PROT_WRITE,
+ MAP_SHARED, shmem_fd, 0);
+ ATF_REQUIRE(shmem != MAP_FAILED);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ struct kevent kev;
+ struct kevent out_kev;
+
+ /* Fires immediately because value is not as expected. */
+ *shmem = 0;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Doesn't fire immediately because value is as expected. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /*
+ * Re-adding, with a different expected value. This causes it to fire
+ * (not because the expected value doesn't match *shmem, but because it
+ * doesn't match what we originally added).
+ */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 43, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Try to wake it using _umtx_op. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* No event should be received (it was one-shot). */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Add it again, with the right value, this time not one-shot. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Try to wake it with the right address. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* And again. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken again, it's still there... */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Delete it. */
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_DELETE,
+ NOTE_USERMEM_LONG, 0, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Shouldn't be woken, now deleted. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* Add it again, with the right value. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Try to wake some other umtx address. */
+ rv = _umtx_op(shmem + 1, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Shouldn't be woken, because that was the wrong address. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* Try to wake it with the right address. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Try to wake it again. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Shouldn't be woken, because it was one-shot. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* Add. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it with NOTE_TRIGGER sent to the same kqueue. */
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ANON,
+ NOTE_TRIGGER, 1, 0);
+ rv = kevent(kq, &kev, 1, NULL, 0, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Add. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it with NOTE_TRIGGER sent with another kqueue. */
+ int kq2 = kqueue();
+ ATF_REQUIRE(kq2 >= 0);
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ANON,
+ NOTE_TRIGGER, 1, 0);
+ rv = kevent(kq2, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Add to both kqueues, not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq2, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it with NOTE_TRIGGER sent using one of them. */
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ANON,
+ NOTE_TRIGGER, 1000, 0);
+ rv = kevent(kq, &kev, 1, NULL, 0, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken in both kqueues. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+ rv = kevent(kq2, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Add. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it with NOTE_TRIGGER sent with KQUEUE_FD_ANON */
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ANON,
+ NOTE_TRIGGER, 1000, 0);
+ rv = kevent(KQUEUE_FD_ANON, &kev, 1, NULL, 0, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Add. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it from another process. */
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* Child wakes. */
+ _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ _Exit(0);
+ } else {
+ /* Parent waits for child. */
+ ATF_REQUIRE(pid > 0);
+ int status;
+ wait(&status);
+ }
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Try to wake it again. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Shouldn't be woken, because it was one-shot. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* Add, in process-private namespace. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG | NOTE_USERMEM_PRIVATE, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Try to wake it from another process. */
+ pid = fork();
+ if (pid == 0) {
+ /* Child wakes. */
+ _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ _Exit(0);
+ } else {
+ /* Parent waits for child. */
+ ATF_REQUIRE(pid > 0);
+ int status;
+ wait(&status);
+ }
+
+ /* Shouldn't be woken, because other process couldn't see it. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* This process can see it. */
+ _umtx_op(shmem, UMTX_OP_WAKE_PRIVATE, 1, NULL, NULL);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, main);
+
+ return (atf_no_error());
+}

File Metadata

Mime Type
text/plain
Expires
Sun, Mar 1, 1:22 PM (15 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29114966
Default Alt Text
D37102.diff (23 KB)

Event Timeline