Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F161307104
D56223.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
4 KB
Referenced Files
None
Subscribers
None
D56223.diff
View Options
diff --git a/tests/sys/kqueue/kqueue_fork.c b/tests/sys/kqueue/kqueue_fork.c
--- a/tests/sys/kqueue/kqueue_fork.c
+++ b/tests/sys/kqueue/kqueue_fork.c
@@ -27,9 +27,13 @@
*/
#include <sys/event.h>
+#include <sys/procdesc.h>
+#include <sys/stat.h>
+#include <sys/user.h>
#include <sys/wait.h>
#include <err.h>
+#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
@@ -81,9 +85,145 @@
ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
}
+#define TIMER_FORKED 0
+#define TIMER_TIMEOUT 1
+
+#define RECV_TIMER 0x01
+#define RECV_VNODE 0x02
+#define RECV_CLOREAD 0x04
+#define RECV_ERROR 0x80
+#define RECV_ALL (RECV_TIMER | RECV_VNODE)
+
+static int
+cponfork_notes_check(int kq, int clofd)
+{
+ struct kevent ev;
+ int error, received = 0;
+
+ EV_SET(&ev, TIMER_TIMEOUT, EVFILT_TIMER,
+ EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_SECONDS, 4, NULL);
+ error = kevent(kq, &ev, 1, NULL, 0, NULL);
+ if (error == -1)
+ return (RECV_ERROR);
+
+ while ((received & RECV_ALL) != RECV_ALL) {
+ error = kevent(kq, NULL, 0, &ev, 1, NULL);
+ if (error < 0)
+ return (RECV_ERROR);
+ else if (error == 0)
+ break;
+
+ switch (ev.filter) {
+ case EVFILT_TIMER:
+ if (ev.ident == TIMER_TIMEOUT)
+ return (received | RECV_ERROR);
+
+ received |= RECV_TIMER;
+ break;
+ case EVFILT_VNODE:
+ received |= RECV_VNODE;
+ break;
+ case EVFILT_READ:
+ if ((int)ev.ident != clofd)
+ return (received | RECV_ERROR);
+ received |= RECV_CLOREAD;
+ break;
+ }
+ }
+
+ return (received);
+}
+
+ATF_TC_WITHOUT_HEAD(cponfork_notes);
+ATF_TC_BODY(cponfork_notes, tc)
+{
+ struct kevent ev[3];
+ int clofd, dfd, error, kq, pdfd, pmask, status;
+ pid_t pid;
+
+ kq = kqueuex(KQUEUE_CPONFORK);
+ ATF_REQUIRE(kq >= 0);
+
+ dfd = open(".", O_DIRECTORY);
+ ATF_REQUIRE(dfd >= 0);
+
+ clofd = kqueue();
+ ATF_REQUIRE(clofd >= 0);
+
+ /*
+ * Setup an event on clofd that we can trigger to make it readable,
+ * as we'll want this ready to go when we fork to be sure that if we
+ * *were* going to receive an event from it, it would have occurred
+ * before the three-second timer that would normally close out the child
+ * fires.
+ */
+ EV_SET(&ev[0], 0, EVFILT_USER, EV_ADD | EV_ENABLE, 0, 0, NULL);
+ error = kevent(clofd, &ev[0], 1, NULL, 0, NULL);
+ ATF_REQUIRE(error != -1);
+
+ /*
+ * Every event we setup here we should expect to observe in both the
+ * child and the parent, with exception to the EVFILT_READ of clofd. We
+ * except that one to be dropped in the child when the kqueue it's
+ * attached to goes away, thus its exclusion from the RECV_ALL mask.
+ */
+ EV_SET(&ev[0], dfd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_WRITE, 0, NULL);
+ EV_SET(&ev[1], TIMER_FORKED, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_SECONDS, 3, NULL);
+ EV_SET(&ev[2], clofd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0,
+ 0, NULL);
+ error = kevent(kq, &ev[0], 3, NULL, 0, NULL);
+ ATF_REQUIRE(error != -1);
+
+ /* Fire off an event to make clofd readable. */
+ EV_SET(&ev[0], 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
+ error = kevent(clofd, &ev[0], 1, NULL, 0, NULL);
+
+ /*
+ * We're only using pdfork here for the kill-on-exit semantics, in case
+ * the parent fails to setup some context needed for one of our events
+ * to fire.
+ */
+ pid = pdfork(&pdfd, 0);
+ ATF_REQUIRE(pid != -1);
+ if (pid == 0) {
+ struct kinfo_file kf = { .kf_structsize = sizeof(kf) };
+
+ if (fcntl(kq, F_KINFO, &kf) != 0)
+ _exit(RECV_ERROR);
+ else if (kf.kf_type != KF_TYPE_KQUEUE)
+ _exit(RECV_ERROR);
+
+ _exit(cponfork_notes_check(kq, clofd));
+ }
+
+ /* Setup anything we need to fire off any of our events above. */
+ error = mkdir("canary", 0755);
+ ATF_REQUIRE(error == 0);
+
+ /*
+ * We'll simultaneously do the same exercise of polling the kqueue in
+ * the parent, to demonstrate that forking doesn't "steal" any of the
+ * knotes from us -- all of the events we've added are one-shot and
+ * still fire twice (once in parent, once in child).
+ */
+ pmask = cponfork_notes_check(kq, clofd);
+ ATF_REQUIRE_EQ(pmask, RECV_ALL | RECV_CLOREAD);
+
+ /* Wait for the child to timeout or observe the timer. */
+ _Static_assert(RECV_ALL <= UCHAR_MAX,
+ "Too many events to observe -- switch from waitpid -> waitid");
+ error = waitpid(pid, &status, 0);
+ ATF_REQUIRE(error != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), RECV_ALL);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, shared_table_filt_sig);
+ ATF_TP_ADD_TC(tp, cponfork_notes);
return (atf_no_error());
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jul 3, 3:27 PM (11 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31007805
Default Alt Text
D56223.diff (4 KB)
Attached To
Mode
D56223: tests: kqueue: add a basic test for CPONFORK
Attached
Detach File
Event Timeline
Log In to Comment