git: fe9bf4b9f580 - main - tests/unix_stream: improve writable test
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 30 Apr 2025 06:01:13 UTC
The branch main has been updated by glebius:
URL: https://cgit.FreeBSD.org/src/commit/?id=fe9bf4b9f580d718c5fc936ccb2c9feb225b3f22
commit fe9bf4b9f580d718c5fc936ccb2c9feb225b3f22
Author: Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2025-04-30 05:31:41 +0000
Commit: Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2025-04-30 05:31:41 +0000
tests/unix_stream: improve writable test
In addition with non-blocking checks on writability, add blocking once
with help of pthread(3).
---
tests/sys/kern/Makefile | 1 +
tests/sys/kern/unix_stream.c | 165 +++++++++++++++++++++++++++++++++++++------
2 files changed, 144 insertions(+), 22 deletions(-)
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
index 900c9a5b3bbe..0dc7fcbdd8d7 100644
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -92,6 +92,7 @@ LIBADD.sendfile_helper+= pthread
LIBADD.fdgrowtable_test+= util pthread kvm procstat
LIBADD.sigwait+= rt
LIBADD.ktrace_test+= sysdecode
+LIBADD.unix_stream+= pthread
NETBSD_ATF_TESTS_C+= lockf_test
NETBSD_ATF_TESTS_C+= mqueue_test
diff --git a/tests/sys/kern/unix_stream.c b/tests/sys/kern/unix_stream.c
index d93bbeff4e41..7aedf462dcce 100644
--- a/tests/sys/kern/unix_stream.c
+++ b/tests/sys/kern/unix_stream.c
@@ -35,6 +35,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
+#include <pthread.h>
+#include <pthread_np.h>
#include <atf-c.h>
@@ -100,47 +102,73 @@ ATF_TC_BODY(send_0, tc)
}
static void
-check_writable(int fd, int expect)
+check_writable_select(int fd, int expect, bool timeout)
{
fd_set wrfds;
- struct pollfd pfd[1];
- struct kevent kev;
- int nfds, kq;
+ int nfds;
FD_ZERO(&wrfds);
FD_SET(fd, &wrfds);
- nfds = select(fd + 1, NULL, &wrfds, NULL,
- &(struct timeval){.tv_usec = 1000});
+ nfds = select(fd + 1, NULL, &wrfds, NULL, timeout ?
+ &(struct timeval){.tv_usec = 1000} : NULL);
ATF_REQUIRE_MSG(nfds == expect,
"select() returns %d errno %d", nfds, errno);
+}
+
+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, 1);
+ 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)
+{
+ struct kevent kev;
+ int nfds, kq;
ATF_REQUIRE(kq = kqueue());
EV_SET(&kev, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
- ATF_REQUIRE(kevent(kq, &kev, 1, NULL, 0, NULL) == 0);
- nfds = kevent(kq, NULL, 0, &kev, 1,
- &(struct timespec){.tv_nsec = 1000000});
+ 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 ?
+ &(struct timespec){.tv_nsec = 1000000} : NULL);
ATF_REQUIRE_MSG(nfds == expect,
- "kevent() returns %d errno %d", nfds, errno);
+ "kevent() returns %d errno %d", nfds, errno);
close(kq);
}
-/*
- * Make sure that a full socket is not reported as writable by event APIs.
- */
-ATF_TC_WITHOUT_HEAD(full_not_writable);
-ATF_TC_BODY(full_not_writable, tc)
+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)
{
void *buf;
u_long sendspace;
- int sv[2];
sendspace = getsendspace();
ATF_REQUIRE((buf = malloc(sendspace)) != NULL);
@@ -149,24 +177,117 @@ ATF_TC_BODY(full_not_writable, tc)
do {} while (send(sv[0], buf, sendspace, 0) == (ssize_t)sendspace);
ATF_REQUIRE(errno == EAGAIN);
ATF_REQUIRE(fcntl(sv[0], F_SETFL, 0) != -1);
+ free(buf);
+}
+
+static void
+full_writability_check(int *sv, check_writable_func_t method)
+{
+ struct check_writable_ctx ctx = {
+ .method = method,
+ .fd = sv[0],
+ };
+ pthread_t thr;
+ void *buf;
+ u_long space;
+
+ space = getsendspace() / 2;
+ ATF_REQUIRE((buf = malloc(space)) != NULL);
- check_writable(sv[0], 0);
+ /* First check with timeout, expecting 0 fds returned. */
+ method(sv[0], 0, true);
- /* Read some data and re-check. */
- ATF_REQUIRE(read(sv[1], buf, sendspace / 2) == (ssize_t)sendspace / 2);
+ /* Launch blocking thread. */
+ ATF_REQUIRE(pthread_create(&thr, NULL, check_writable_blocking_thread,
+ &ctx) == 0);
- check_writable(sv[0], 1);
+ /* 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);
+
+ /* Now check that thread was successfully woken up and exited. */
+ ATF_REQUIRE(pthread_join(thr, NULL) == 0);
+
+ close(sv[0]);
+ close(sv[1]);
free(buf);
+}
+
+/*
+ * Make sure that a full socket is not reported as writable by event APIs.
+ */
+ATF_TC_WITHOUT_HEAD(full_writability_select);
+ATF_TC_BODY(full_writability_select, tc)
+{
+ int sv[2];
+
+ full_socketpair(sv);
+ full_writability_check(sv, check_writable_select);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(full_writability_poll);
+ATF_TC_BODY(full_writability_poll, tc)
+{
+ int sv[2];
+
+ full_socketpair(sv);
+ full_writability_check(sv, check_writable_poll);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(full_writability_kevent);
+ATF_TC_BODY(full_writability_kevent, tc)
+{
+ int sv[2];
+
+ full_socketpair(sv);
+ full_writability_check(sv, check_writable_kevent);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(connected_writability);
+ATF_TC_BODY(connected_writability, tc)
+{
+ int sv[2];
+
+ 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]);
}
+ATF_TC_WITHOUT_HEAD(unconnected_writability);
+ATF_TC_BODY(unconnected_writability, tc)
+{
+ int s;
+
+ 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);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, getpeereid);
ATF_TP_ADD_TC(tp, send_0);
- ATF_TP_ADD_TC(tp, full_not_writable);
+ ATF_TP_ADD_TC(tp, connected_writability);
+ ATF_TP_ADD_TC(tp, unconnected_writability);
+ ATF_TP_ADD_TC(tp, full_writability_select);
+ ATF_TP_ADD_TC(tp, full_writability_poll);
+ ATF_TP_ADD_TC(tp, full_writability_kevent);
return atf_no_error();
}