1 #include <unistd.h>
2 #include <pthread.h>
3 #include <errno.h>
4
5 #include <sys/event.h>
6 #include <mach/mach.h>
7 #include <mach/mach_port.h>
8
9 #include <Block.h>
10 #include <darwintest.h>
11
12 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
13
14 /*
15 * <rdar://problem/30231213> close() of kqueue FD races with kqueue_scan park
16 *
17 * When close concurrent with poll goes wrong, the close hangs
18 * and the kevent never gets any more events.
19 */
20
21 /* Both events should fire at about the same time */
22 static uint32_t timeout_ms = 10;
23
24 static void *
poll_kqueue(void * arg)25 poll_kqueue(void *arg)
26 {
27 int fd = (int)arg;
28
29 struct kevent kev = {
30 .filter = EVFILT_TIMER,
31 .flags = EV_ADD,
32 .data = timeout_ms,
33 };
34
35 int rv = kevent(fd, &kev, 1, NULL, 0, NULL);
36
37 if (rv == -1 && errno == EBADF) {
38 /* The close may race with this thread spawning */
39 T_LOG("kqueue already closed?");
40 return NULL;
41 } else {
42 T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kevent");
43 }
44
45 while ((rv = kevent(fd, NULL, 0, &kev, 1, NULL)) == 1) {
46 T_LOG("poll\n");
47 }
48
49 if (rv != -1 || errno != EBADF) {
50 T_ASSERT_POSIX_SUCCESS(rv, "fd should be closed");
51 }
52
53 return NULL;
54 }
55
56 static void
run_test(void)57 run_test(void)
58 {
59 int fd = kqueue();
60 T_QUIET; T_ASSERT_POSIX_SUCCESS(fd, "kqueue");
61
62 pthread_t thread;
63 int rv = pthread_create(&thread, NULL, poll_kqueue,
64 (void *)(uintptr_t)fd);
65 T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_create");
66
67 usleep(timeout_ms * 1000);
68
69 rv = close(fd);
70 T_ASSERT_POSIX_SUCCESS(rv, "close");
71
72 rv = pthread_join(thread, NULL);
73 T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_join");
74 }
75
76 T_DECL(kqueue_close_race, "Races kqueue close with kqueue process",
77 T_META_LTEPHASE(LTE_POSTINIT), T_META_TIMEOUT(5), T_META_TAG_VM_PREFERRED)
78 {
79 for (uint32_t i = 1; i < 100; i++) {
80 run_test();
81 }
82 }
83
84 static void *
pthread_async_do(void * arg)85 pthread_async_do(void *arg)
86 {
87 void (^block)(void) = arg;
88 block();
89 Block_release(block);
90 pthread_detach(pthread_self());
91 return NULL;
92 }
93
94 static void
95 pthread_async(void (^block)(void))
96 {
97 pthread_t th;
98 int rc;
99
100 rc = pthread_create(&th, NULL, pthread_async_do, Block_copy(block));
101 T_QUIET; T_ASSERT_POSIX_SUCCESS(rc, "pthread_create");
102 }
103
104 T_DECL(kqueue_filter_close,
105 "Check closing a kqfile with various filters works, rdar://72542450", T_META_TAG_VM_PREFERRED)
106 {
107 mach_port_t mp, pset;
108 kern_return_t kr;
109 struct kevent ke;
110 int kq, rc;
111 int pfd[2];
112
113 kq = kqueue();
114 T_ASSERT_POSIX_SUCCESS(kq, "kqueue()");
115
116 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
117 T_QUIET; T_EXPECT_MACH_SUCCESS(kr, "mach_port_allocate(RECEIVE)");
118
119 ke = (struct kevent){
120 .filter = EVFILT_MACHPORT,
121 .flags = EV_ADD,
122 .ident = mp,
123 };
124 rc = kevent(kq, &ke, 1, NULL, 0, NULL);
125 T_EXPECT_POSIX_SUCCESS(rc, "kevent(mp)");
126
127 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &pset);
128 T_QUIET; T_EXPECT_MACH_SUCCESS(kr, "mach_port_allocate(PSET)");
129
130 ke = (struct kevent){
131 .filter = EVFILT_MACHPORT,
132 .flags = EV_ADD,
133 .ident = pset,
134 };
135 rc = kevent(kq, &ke, 1, NULL, 0, NULL);
136 T_EXPECT_POSIX_SUCCESS(rc, "kevent(pset)");
137
138 rc = pipe(pfd);
139 T_EXPECT_POSIX_SUCCESS(rc, "pipe");
140
141 ke = (struct kevent){
142 .filter = EVFILT_READ,
143 .flags = EV_ADD,
144 .ident = (unsigned long)pfd[0],
145 };
146 rc = kevent(kq, &ke, 1, NULL, 0, NULL);
147 T_EXPECT_POSIX_SUCCESS(rc, "kevent(fd)");
148
149 pthread_async(^{
150 sleep(1);
151 T_EXPECT_POSIX_SUCCESS(close(kq), "close");
152 });
153
154 rc = kevent(kq, NULL, 0, &ke, 1, NULL);
155 T_EXPECT_POSIX_FAILURE(rc, EBADF, "kevent(closed fd) returns EBADF");
156 }
157