The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=19e599c0e00dcf0699c38798e5d58fb8423e5810
commit 19e599c0e00dcf0699c38798e5d58fb8423e5810 Author: Gleb Smirnoff <gleb...@freebsd.org> AuthorDate: 2025-06-10 02:59:31 +0000 Commit: Gleb Smirnoff <gleb...@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)