Page MenuHomeFreeBSD

D35155.diff
No OneTemporary

D35155.diff

Index: lib/libc/sys/kqueue.2
===================================================================
--- lib/libc/sys/kqueue.2
+++ lib/libc/sys/kqueue.2
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 13, 2022
+.Dd May 10, 2022
.Dt KQUEUE 2
.Os
.Sh NAME
@@ -278,6 +278,16 @@
.Dv EV_ENABLE .
This flag may not be used with
.Dv EV_ADD .
+.It Dv EV_EXCLUSIVE
+Sets an exclusive wakeup mode for the kernel event queue, assotiated
+with descriptor
+.Fa kq ,
+and kevent, identified by pair
+.Fa ident
+and
+.Fa filter .
+When an event occurs and multiple waiters present, only one of them
+will receive an event.
.El
.Pp
The predefined system filters are listed below.
Index: sys/compat/linux/linux_event.h
===================================================================
--- sys/compat/linux/linux_event.h
+++ sys/compat/linux/linux_event.h
@@ -40,15 +40,17 @@
#define LINUX_EPOLLERR 0x008
#define LINUX_EPOLLHUP 0x010
#define LINUX_EPOLLRDHUP 0x2000
-#define LINUX_EPOLLWAKEUP 1u<<29
-#define LINUX_EPOLLONESHOT 1u<<30
-#define LINUX_EPOLLET 1u<<31
+#define LINUX_EPOLLEXCLUSIVE (1u<<28)
+#define LINUX_EPOLLWAKEUP (1u<<29)
+#define LINUX_EPOLLONESHOT (1u<<30)
+#define LINUX_EPOLLET (1u<<31)
#define LINUX_EPOLL_EVRD (LINUX_EPOLLIN|LINUX_EPOLLRDNORM)
#define LINUX_EPOLL_EVWR (LINUX_EPOLLOUT|LINUX_EPOLLWRNORM)
#define LINUX_EPOLL_EVSUP (LINUX_EPOLLET|LINUX_EPOLLONESHOT \
|LINUX_EPOLLHUP|LINUX_EPOLLERR|LINUX_EPOLLPRI \
- |LINUX_EPOLL_EVRD|LINUX_EPOLL_EVWR|LINUX_EPOLLRDHUP)
+ |LINUX_EPOLL_EVRD|LINUX_EPOLL_EVWR|LINUX_EPOLLRDHUP \
+ |LINUX_EPOLLEXCLUSIVE)
#define LINUX_EPOLL_CTL_ADD 1
#define LINUX_EPOLL_CTL_DEL 2
Index: sys/compat/linux/linux_event.c
===================================================================
--- sys/compat/linux/linux_event.c
+++ sys/compat/linux/linux_event.c
@@ -214,6 +214,8 @@
kev_flags |= EV_ERROR;
if ((levents & LINUX_EPOLLRDHUP) != 0)
kev_flags |= EV_EOF;
+ if ((levents & LINUX_EPOLLEXCLUSIVE) != 0)
+ kev_flags |= EV_EXCLUSIVE;
/* flags related to what event is registered */
if ((levents & LINUX_EPOLL_EVRD) != 0) {
Index: sys/kern/kern_event.c
===================================================================
--- sys/kern/kern_event.c
+++ sys/kern/kern_event.c
@@ -2071,6 +2071,9 @@
kn->kn_status |= KN_DISABLED;
kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE);
kq->kq_count--;
+ } else if (kn->kn_flags & EV_EXCLUSIVE) {
+ kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE);
+ kq->kq_count--;
} else
TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
Index: sys/sys/event.h
===================================================================
--- sys/sys/event.h
+++ sys/sys/event.h
@@ -145,6 +145,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_EXCLUSIVE 0x0400 /* report kevent for one waiter */
#define EV_SYSFLAGS 0xF000 /* reserved by system */
#define EV_DROP 0x1000 /* note should be dropped */
Index: tests/sys/kqueue/Makefile
===================================================================
--- tests/sys/kqueue/Makefile
+++ tests/sys/kqueue/Makefile
@@ -5,6 +5,7 @@
TESTSDIR= ${TESTSBASE}/sys/kqueue
BINDIR= ${TESTSDIR}
+ATF_TESTS_C+= kevent_exclusive
ATF_TESTS_C+= kqueue_peek_signal
NETBSD_ATF_TESTS_C= proc1_test
@@ -14,6 +15,8 @@
NETBSD_ATF_TESTS_C+= sig_test
NETBSD_ATF_TESTS_C+= vnode_test
+LIBADD.kevent_exclusive+= pthread
+
WARNS?= 3
TESTS_SUBDIRS+= libkqueue
Index: tests/sys/kqueue/kevent_exclusive.c
===================================================================
--- /dev/null
+++ tests/sys/kqueue/kevent_exclusive.c
@@ -0,0 +1,295 @@
+/*-
+ * Copyright 2022 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <sys/cdefs.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <pthread.h>
+
+#include <atf-c.h>
+
+static int kq;
+static int evagain;
+static int evfired;
+static int evwblock;
+static int evaccpt;
+
+#define SERVER_PATH "server_kevexcl"
+
+#ifndef EV_EXCLUSIVE
+#define EV_EXCLUSIVE 0x0400
+#endif
+
+static inline struct timespec
+make_timespec(time_t s, long int ns)
+{
+ struct timespec rts;
+
+ rts.tv_sec = s;
+ rts.tv_nsec = ns;
+ return (rts);
+}
+
+static void *
+waiter(void *arg)
+{
+ struct kevent tevent;
+ struct timespec timeout;
+ int afd;
+ ssize_t ec;
+
+ timeout = make_timespec(0, 5000000);
+
+ ec = kevent(kq, NULL, 0, &tevent, 1, &timeout);
+ if (ec == 0) {
+ evwblock++;
+ return (NULL);
+ }
+ ATF_REQUIRE_EQ(1, ec);
+ evfired++;
+
+ afd = accept((int)tevent.ident, NULL, NULL);
+ if (afd == -1 && errno == EAGAIN)
+ evagain++;
+ else if (afd > 0) {
+ evaccpt++;
+ ATF_REQUIRE_EQ(0, close(afd));
+ }
+ return (NULL);
+}
+
+static void *
+waiter2(void *arg)
+{
+ struct kevent tevent;
+ int afd;
+ ssize_t ec;
+
+ ec = kevent(kq, NULL, 0, &tevent, 1, NULL);
+ ATF_REQUIRE_EQ(1, ec);
+ evfired++;
+
+ afd = accept((int)tevent.ident, NULL, NULL);
+ ATF_REQUIRE(afd > 0);
+ evaccpt++;
+ ATF_REQUIRE_EQ(0, close(afd));
+ return (NULL);
+}
+
+ATF_TC_WITH_CLEANUP(exclusiveon);
+ATF_TC_BODY(exclusiveon, tc)
+{
+ struct sockaddr_un server;
+ socklen_t len;
+ struct kevent event;
+ pthread_t w0, w1;
+ int srvfd, clfd;
+
+ evagain = evfired = evwblock = evaccpt = 0;
+
+ /* server */
+ len = sizeof(struct sockaddr_un);
+ memset(&server, 0, len);
+ server.sun_family = AF_UNIX;
+ strcpy(server.sun_path, SERVER_PATH);
+
+ srvfd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ ATF_REQUIRE(srvfd > 0);
+ ATF_REQUIRE_EQ(0, bind(srvfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, listen(srvfd, 5));
+
+ kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ EV_SET(&event, srvfd, EVFILT_READ, EV_ADD | EV_EXCLUSIVE,
+ 0, 0, NULL);
+
+ ATF_REQUIRE_EQ(0, kevent(kq, &event, 1, NULL, 0, NULL));
+
+ ATF_REQUIRE_EQ(0, pthread_create(&w0, NULL, waiter,
+ (void *) (uintptr_t) 1));
+ ATF_REQUIRE_EQ(0, pthread_create(&w1, NULL, waiter,
+ (void *) (uintptr_t) 2));
+
+ clfd = socket(PF_UNIX, SOCK_STREAM, 0);
+ ATF_REQUIRE(clfd > 0);
+
+ ATF_REQUIRE_EQ(0, connect(clfd, (struct sockaddr *)&server, len));
+
+ ATF_REQUIRE_EQ(0, pthread_join(w0, NULL));
+ ATF_REQUIRE_EQ(0, pthread_join(w1, NULL));
+
+ ATF_REQUIRE_EQ(0, evagain);
+ ATF_REQUIRE_EQ(1, evfired);
+ ATF_REQUIRE_EQ(1, evwblock);
+ ATF_REQUIRE_EQ(1, evaccpt);
+
+ ATF_REQUIRE_EQ(0, close(clfd));
+ ATF_REQUIRE_EQ(0, close(srvfd));
+ ATF_REQUIRE_EQ(0, close(kq));
+ ATF_REQUIRE_EQ(0, unlink(SERVER_PATH));
+}
+
+ATF_TC_HEAD(exclusiveon, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check waleups if EV_EXCLUSIVE is set");
+}
+
+ATF_TC_CLEANUP(exclusiveon, tc)
+{
+
+ unlink(SERVER_PATH);
+}
+
+ATF_TC_WITH_CLEANUP(exclusiveoff);
+ATF_TC_BODY(exclusiveoff, tc)
+{
+ struct sockaddr_un server;
+ socklen_t len;
+ struct kevent event;
+ pthread_t w0, w1;
+ int srvfd, clfd;
+
+ evagain = evfired = evwblock = evaccpt = 0;
+
+ /* server */
+ len = sizeof(struct sockaddr_un);
+ memset(&server, 0, len);
+ server.sun_family = AF_UNIX;
+ strcpy(server.sun_path, SERVER_PATH);
+
+ srvfd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ ATF_REQUIRE(srvfd > 0);
+ ATF_REQUIRE_EQ(0, bind(srvfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, listen(srvfd, 5));
+
+ kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ EV_SET(&event, srvfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
+
+ ATF_REQUIRE_EQ(0, kevent(kq, &event, 1, NULL, 0, NULL));
+
+ ATF_REQUIRE_EQ(0, pthread_create(&w0, NULL, waiter, NULL));
+ ATF_REQUIRE_EQ(0, pthread_create(&w1, NULL, waiter, NULL));
+
+ clfd = socket(PF_UNIX, SOCK_STREAM, 0);
+ ATF_REQUIRE(clfd > 0);
+
+ ATF_REQUIRE_EQ(0, connect(clfd, (struct sockaddr *)&server, len));
+
+ ATF_REQUIRE_EQ(0, pthread_join(w0, NULL));
+ ATF_REQUIRE_EQ(0, pthread_join(w1, NULL));
+
+ ATF_REQUIRE_EQ(1, evagain);
+ ATF_REQUIRE_EQ(2, evfired);
+ ATF_REQUIRE_EQ(0, evwblock);
+ ATF_REQUIRE_EQ(1, evaccpt);
+
+ ATF_REQUIRE_EQ(0, close(clfd));
+ ATF_REQUIRE_EQ(0, close(srvfd));
+ ATF_REQUIRE_EQ(0, close(kq));
+ ATF_REQUIRE_EQ(0, unlink(SERVER_PATH));
+}
+
+ATF_TC_HEAD(exclusiveoff, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check waleups if EV_EXCLUSIVE is not set");
+}
+
+ATF_TC_CLEANUP(exclusiveoff, tc)
+{
+
+ unlink(SERVER_PATH);
+}
+
+ATF_TC_WITH_CLEANUP(exclusiveon2);
+ATF_TC_BODY(exclusiveon2, tc)
+{
+ struct sockaddr_un server;
+ socklen_t len;
+ struct kevent event;
+ pthread_t w0, w1;
+ int srvfd, clfd;
+
+ evagain = evfired = evwblock = evaccpt = 0;
+
+ /* server */
+ len = sizeof(struct sockaddr_un);
+ memset(&server, 0, len);
+ server.sun_family = AF_UNIX;
+ strcpy(server.sun_path, SERVER_PATH);
+
+ srvfd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ ATF_REQUIRE(srvfd > 0);
+ ATF_REQUIRE_EQ(0, bind(srvfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, listen(srvfd, 5));
+
+ kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ EV_SET(&event, srvfd, EVFILT_READ, EV_ADD | EV_EXCLUSIVE,
+ 0, 0, NULL);
+
+ ATF_REQUIRE_EQ(0, kevent(kq, &event, 1, NULL, 0, NULL));
+
+ ATF_REQUIRE_EQ(0, pthread_create(&w0, NULL, waiter2,
+ (void *) (uintptr_t) 1));
+ ATF_REQUIRE_EQ(0, pthread_create(&w1, NULL, waiter2,
+ (void *) (uintptr_t) 2));
+
+ clfd = socket(PF_UNIX, SOCK_STREAM, 0);
+ ATF_REQUIRE(clfd > 0);
+ ATF_REQUIRE_EQ(0, connect(clfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, close(clfd));
+
+ clfd = socket(PF_UNIX, SOCK_STREAM, 0);
+ ATF_REQUIRE(clfd > 0);
+ ATF_REQUIRE_EQ(0, connect(clfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, close(clfd));
+
+ ATF_REQUIRE_EQ(0, pthread_join(w0, NULL));
+ ATF_REQUIRE_EQ(0, pthread_join(w1, NULL));
+
+ ATF_REQUIRE_EQ(0, evagain);
+ ATF_REQUIRE_EQ(2, evfired);
+ ATF_REQUIRE_EQ(0, evwblock);
+ ATF_REQUIRE_EQ(2, evaccpt);
+
+ ATF_REQUIRE_EQ(0, close(srvfd));
+ ATF_REQUIRE_EQ(0, close(kq));
+ ATF_REQUIRE_EQ(0, unlink(SERVER_PATH));
+}
+
+ATF_TC_HEAD(exclusiveon2, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check waleups if EV_EXCLUSIVE is set");
+}
+
+ATF_TC_CLEANUP(exclusiveon2, tc)
+{
+
+ unlink(SERVER_PATH);
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ /*
+ * Thundering herd & EV_EXCLUSIVE
+ */
+ ATF_TP_ADD_TC(tp, exclusiveon);
+ ATF_TP_ADD_TC(tp, exclusiveoff);
+
+ ATF_TP_ADD_TC(tp, exclusiveon2);
+
+ return (atf_no_error());
+}

File Metadata

Mime Type
text/plain
Expires
Thu, Nov 27, 9:31 AM (7 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26252021
Default Alt Text
D35155.diff (10 KB)

Event Timeline