git: 19e599c0e00d - main - tests/unix_stream: refactor event mech tests
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 10 Jun 2025 06:06:06 UTC
The branch main has been updated by glebius:
URL: https://cgit.FreeBSD.org/src/commit/?id=19e599c0e00dcf0699c38798e5d58fb8423e5810
commit 19e599c0e00dcf0699c38798e5d58fb8423e5810
Author: Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2025-06-10 02:59:31 +0000
Commit: Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2025-06-10 04:57:04 +0000
tests/unix_stream: refactor event mech tests
Provide a context that can be used both for writability and readability
checks. This collapses two check functions per mech into one. Provide a
possibility to check advanced features of an event mech, e.g. returned
kev_flags for kevent(2).
Should be no functional change.
---
tests/sys/kern/unix_stream.c | 293 ++++++++++++++++++++++++-------------------
1 file changed, 161 insertions(+), 132 deletions(-)
diff --git a/tests/sys/kern/unix_stream.c b/tests/sys/kern/unix_stream.c
index f8ba288308bd..72dbe8ca1d92 100644
--- a/tests/sys/kern/unix_stream.c
+++ b/tests/sys/kern/unix_stream.c
@@ -102,98 +102,78 @@ ATF_TC_BODY(send_0, tc)
close(sv[1]);
}
-static void
-check_readable_select(int fd, int expect, bool timeout)
-{
- fd_set rdfds;
- int nfds;
-
- FD_ZERO(&rdfds);
- FD_SET(fd, &rdfds);
- nfds = select(fd + 1, &rdfds, NULL, NULL, timeout ?
- &(struct timeval){.tv_usec = 1000} : NULL);
- ATF_REQUIRE_MSG(nfds == expect,
- "select() returns %d errno %d", nfds, errno);
-}
+struct check_ctx;
+typedef void check_func_t(struct check_ctx *);
+struct check_ctx {
+ check_func_t *method;
+ int sv[2];
+ bool timeout;
+ union {
+ enum { SELECT_RD, SELECT_WR } select_what;
+ short poll_events;
+ short kev_filter;
+ };
+ int nfds;
+ union {
+ short poll_revents;
+ unsigned short kev_flags;
+ };
+};
static void
-check_writable_select(int fd, int expect, bool timeout)
+check_select(struct check_ctx *ctx)
{
- fd_set wrfds;
+ fd_set fds;
int nfds;
- FD_ZERO(&wrfds);
- FD_SET(fd, &wrfds);
- nfds = select(fd + 1, NULL, &wrfds, NULL, timeout ?
- &(struct timeval){.tv_usec = 1000} : NULL);
- ATF_REQUIRE_MSG(nfds == expect,
+ FD_ZERO(&fds);
+ FD_SET(ctx->sv[0], &fds);
+ nfds = select(ctx->sv[0] + 1,
+ ctx->select_what == SELECT_RD ? &fds : NULL,
+ ctx->select_what == SELECT_WR ? &fds : NULL,
+ NULL,
+ ctx->timeout ? &(struct timeval){.tv_usec = 1000} : NULL);
+ ATF_REQUIRE_MSG(nfds == ctx->nfds,
"select() returns %d errno %d", nfds, errno);
}
static void
-check_readable_poll(int fd, int expect, bool timeout)
+check_poll(struct check_ctx *ctx)
{
struct pollfd pfd[1];
int nfds;
pfd[0] = (struct pollfd){
- .fd = fd,
- .events = POLLIN | POLLRDNORM,
+ .fd = ctx->sv[0],
+ .events = ctx->poll_events,
};
- nfds = poll(pfd, 1, timeout ? 1 : INFTIM);
- ATF_REQUIRE_MSG(nfds == expect,
+ nfds = poll(pfd, 1, ctx->timeout ? 1 : INFTIM);
+ ATF_REQUIRE_MSG(nfds == ctx->nfds,
"poll() returns %d errno %d", nfds, errno);
+ ATF_REQUIRE((pfd[0].revents & ctx->poll_revents) == ctx->poll_revents);
}
static void
-check_writable_poll(int fd, int expect, bool timeout)
-{
- struct pollfd pfd[1];
- int nfds;
-
- pfd[0] = (struct pollfd){
- .fd = fd,
- .events = POLLOUT | POLLWRNORM,
- };
- nfds = poll(pfd, 1, timeout ? 1 : INFTIM);
- ATF_REQUIRE_MSG(nfds == expect,
- "poll() returns %d errno %d", nfds, errno);
-}
-
-static void
-check_writable_kevent(int fd, int expect, bool timeout)
+check_kevent(struct check_ctx *ctx)
{
struct kevent kev;
int nfds, kq;
ATF_REQUIRE(kq = kqueue());
- EV_SET(&kev, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+ EV_SET(&kev, ctx->sv[0], ctx->kev_filter, EV_ADD, 0, 0, NULL);
nfds = kevent(kq, &kev, 1, NULL, 0, NULL);
ATF_REQUIRE_MSG(nfds == 0,
"kevent() returns %d errno %d", nfds, errno);
- nfds = kevent(kq, NULL, 0, &kev, 1, timeout ?
+ nfds = kevent(kq, NULL, 0, &kev, 1, ctx->timeout ?
&(struct timespec){.tv_nsec = 1000000} : NULL);
- ATF_REQUIRE_MSG(nfds == expect,
+ ATF_REQUIRE_MSG(nfds == ctx->nfds,
"kevent() returns %d errno %d", nfds, errno);
+ ATF_REQUIRE(kev.ident == (uintptr_t)ctx->sv[0] &&
+ kev.filter == ctx->kev_filter &&
+ (kev.flags & ctx->kev_flags) == ctx->kev_flags);
close(kq);
}
-typedef void check_writable_func_t(int, int, bool);
-struct check_writable_ctx {
- check_writable_func_t *method;
- int fd;
-};
-
-static void *
-check_writable_blocking_thread(void *arg)
-{
- struct check_writable_ctx *ctx = arg;
-
- ctx->method(ctx->fd, 1, false);
-
- return (NULL);
-}
-
static void
full_socketpair(int *sv)
{
@@ -210,13 +190,19 @@ full_socketpair(int *sv)
free(buf);
}
+static void *
+pthread_wrap(void *arg)
+{
+ struct check_ctx *ctx = arg;
+
+ ctx->method(ctx);
+
+ return (NULL);
+}
+
static void
-full_writability_check(int *sv, check_writable_func_t method)
+full_writability_check(struct check_ctx *ctx)
{
- struct check_writable_ctx ctx = {
- .method = method,
- .fd = sv[0],
- };
pthread_t thr;
void *buf;
u_long space;
@@ -225,26 +211,30 @@ full_writability_check(int *sv, check_writable_func_t method)
ATF_REQUIRE((buf = malloc(space)) != NULL);
/* First check with timeout, expecting 0 fds returned. */
- method(sv[0], 0, true);
+ ctx->timeout = true;
+ ctx->nfds = 0;
+ ctx->method(ctx);
/* Launch blocking thread. */
- ATF_REQUIRE(pthread_create(&thr, NULL, check_writable_blocking_thread,
- &ctx) == 0);
+ ctx->timeout = false;
+ ctx->nfds = 1;
+ ATF_REQUIRE(pthread_create(&thr, NULL, pthread_wrap, ctx) == 0);
/* Sleep a bit to make sure that thread is put to sleep. */
usleep(10000);
ATF_REQUIRE(pthread_peekjoin_np(thr, NULL) == EBUSY);
/* Read some data and re-check, the fd is expected to be returned. */
- ATF_REQUIRE(read(sv[1], buf, space) == (ssize_t)space);
-
- method(sv[0], 1, true);
+ ATF_REQUIRE(read(ctx->sv[1], buf, space) == (ssize_t)space);
/* Now check that thread was successfully woken up and exited. */
ATF_REQUIRE(pthread_join(thr, NULL) == 0);
- close(sv[0]);
- close(sv[1]);
+ /* Extra check repeating what joined thread already did. */
+ ctx->method(ctx);
+
+ close(ctx->sv[0]);
+ close(ctx->sv[1]);
free(buf);
}
@@ -254,130 +244,169 @@ full_writability_check(int *sv, check_writable_func_t method)
ATF_TC_WITHOUT_HEAD(full_writability_select);
ATF_TC_BODY(full_writability_select, tc)
{
- int sv[2];
+ struct check_ctx ctx = {
+ .method = check_select,
+ .select_what = SELECT_WR,
+ };
- full_socketpair(sv);
- full_writability_check(sv, check_writable_select);
- close(sv[0]);
- close(sv[1]);
+ full_socketpair(ctx.sv);
+ full_writability_check(&ctx);
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
}
ATF_TC_WITHOUT_HEAD(full_writability_poll);
ATF_TC_BODY(full_writability_poll, tc)
{
- int sv[2];
+ struct check_ctx ctx = {
+ .method = check_poll,
+ .poll_events = POLLOUT | POLLWRNORM,
+ };
- full_socketpair(sv);
- full_writability_check(sv, check_writable_poll);
- close(sv[0]);
- close(sv[1]);
+ full_socketpair(ctx.sv);
+ full_writability_check(&ctx);
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
}
ATF_TC_WITHOUT_HEAD(full_writability_kevent);
ATF_TC_BODY(full_writability_kevent, tc)
{
- int sv[2];
+ struct check_ctx ctx = {
+ .method = check_kevent,
+ .kev_filter = EVFILT_WRITE,
+ };
- full_socketpair(sv);
- full_writability_check(sv, check_writable_kevent);
- close(sv[0]);
- close(sv[1]);
+ full_socketpair(ctx.sv);
+ full_writability_check(&ctx);
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
}
ATF_TC_WITHOUT_HEAD(connected_writability);
ATF_TC_BODY(connected_writability, tc)
{
- int sv[2];
+ struct check_ctx ctx = {
+ .timeout = true,
+ .nfds = 1,
+ };
- do_socketpair(sv);
- check_writable_select(sv[0], 1, true);
- check_writable_poll(sv[0], 1, true);
- check_writable_kevent(sv[0], 1, true);
- close(sv[0]);
- close(sv[1]);
+ do_socketpair(ctx.sv);
+
+ ctx.select_what = SELECT_WR;
+ check_select(&ctx);
+ ctx.poll_events = POLLOUT | POLLWRNORM;
+ check_poll(&ctx);
+ ctx.kev_filter = EVFILT_WRITE;
+ check_kevent(&ctx);
+
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
}
ATF_TC_WITHOUT_HEAD(unconnected_writability);
ATF_TC_BODY(unconnected_writability, tc)
{
- int s;
+ struct check_ctx ctx = {
+ .timeout = true,
+ .nfds = 0,
+ };
+
+ ATF_REQUIRE((ctx.sv[0] = socket(PF_LOCAL, SOCK_STREAM, 0)) > 0);
- ATF_REQUIRE((s = socket(PF_LOCAL, SOCK_STREAM, 0)) > 0);
- check_writable_select(s, 0, true);
- check_writable_poll(s, 0, true);
- check_writable_kevent(s, 0, true);
- close(s);
+ ctx.select_what = SELECT_WR;
+ check_select(&ctx);
+ ctx.poll_events = POLLOUT | POLLWRNORM;
+ check_poll(&ctx);
+ ctx.kev_filter = EVFILT_WRITE;
+ check_kevent(&ctx);
+
+ close(ctx.sv[0]);
}
ATF_TC_WITHOUT_HEAD(peerclosed_writability);
ATF_TC_BODY(peerclosed_writability, tc)
{
- struct kevent kev;
- int sv[2], kq;
-
- do_socketpair(sv);
- close(sv[1]);
+ struct check_ctx ctx = {
+ .timeout = false,
+ .nfds = 1,
+ };
- check_writable_select(sv[0], 1, false);
- check_writable_poll(sv[0], 1, false);
+ do_socketpair(ctx.sv);
+ close(ctx.sv[1]);
- ATF_REQUIRE(kq = kqueue());
- EV_SET(&kev, sv[0], EVFILT_WRITE, EV_ADD, 0, 0, NULL);
- ATF_REQUIRE(kevent(kq, &kev, 1, &kev, 1, NULL) == 1);
- ATF_REQUIRE(kev.ident == (uintptr_t)sv[0] &&
- kev.filter == EVFILT_WRITE &&
- kev.flags == EV_EOF);
+ ctx.select_what = SELECT_WR;
+ check_select(&ctx);
+ ctx.poll_events = POLLOUT | POLLWRNORM;
+ check_poll(&ctx);
+ ctx.kev_filter = EVFILT_WRITE;
+ ctx.kev_flags = EV_EOF;
+ check_kevent(&ctx);
- close(sv[0]);
+ close(ctx.sv[0]);
}
ATF_TC_WITHOUT_HEAD(peershutdown_writability);
ATF_TC_BODY(peershutdown_writability, tc)
{
- int sv[2];
+ struct check_ctx ctx = {
+ .timeout = false,
+ .nfds = 1,
+ };
- do_socketpair(sv);
- shutdown(sv[1], SHUT_RD);
+ do_socketpair(ctx.sv);
+ shutdown(ctx.sv[1], SHUT_RD);
- check_writable_select(sv[0], 1, false);
- check_writable_poll(sv[0], 1, false);
+ ctx.select_what = SELECT_WR;
+ check_select(&ctx);
+ ctx.poll_events = POLLOUT | POLLWRNORM;
+ check_poll(&ctx);
/*
* XXXGL: historically unix(4) sockets were not reporting peer's
* shutdown(SHUT_RD) as our EV_EOF. The kevent(2) manual page says
* "filter will set EV_EOF when the reader disconnects", which is hard
* to interpret unambigously. For now leave the historic behavior,
* but we may want to change that in uipc_usrreq.c:uipc_filt_sowrite(),
- * and then this test will look like the peerclosed_writability test.
+ * and then this test will also expect EV_EOF in returned flags.
*/
- check_writable_kevent(sv[0], 1, false);
+ ctx.kev_filter = EVFILT_WRITE;
+ check_kevent(&ctx);
- close(sv[0]);
- close(sv[1]);
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
}
ATF_TC_WITHOUT_HEAD(peershutdown_readability);
ATF_TC_BODY(peershutdown_readability, tc)
{
+ struct check_ctx ctx = {
+ .timeout = false,
+ .nfds = 1,
+ };
ssize_t readsz;
- int sv[2];
char c;
- do_socketpair(sv);
- shutdown(sv[1], SHUT_WR);
+ do_socketpair(ctx.sv);
+ shutdown(ctx.sv[1], SHUT_WR);
/*
* The other side should flag as readable in select(2) to allow it to
* read(2) and observe EOF. Ensure that both poll(2) and select(2)
* are consistent here.
*/
- check_readable_select(sv[0], 1, false);
- check_readable_poll(sv[0], 1, false);
+ ctx.select_what = SELECT_RD;
+ check_select(&ctx);
+ ctx.poll_events = POLLIN | POLLRDNORM;
+ check_poll(&ctx);
- readsz = read(sv[0], &c, sizeof(c));
+ /*
+ * Also check that read doesn't block.
+ */
+ readsz = read(ctx.sv[0], &c, sizeof(c));
ATF_REQUIRE_INTEQ(0, readsz);
- close(sv[0]);
- close(sv[1]);
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
}
ATF_TP_ADD_TCS(tp)