Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F137846904
D35155.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D35155.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D35155: Add EV_EXCLUSIVE to kevent.
Attached
Detach File
Event Timeline
Log In to Comment