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(
13 T_META_NAMESPACE("xnu.kevent"),
14 T_META_RADAR_COMPONENT_NAME("xnu"),
15 T_META_RADAR_COMPONENT_VERSION("kevent"),
16 T_META_RUN_CONCURRENTLY(true)
17 );
18
19 static void
send(mach_port_t send_port)20 send(mach_port_t send_port)
21 {
22 kern_return_t kr = 0;
23 mach_msg_base_t msg = {
24 .header = {
25 .msgh_remote_port = send_port,
26 .msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
27 0, MACH_MSG_TYPE_MOVE_SEND, 0),
28 .msgh_id = 0x100,
29 .msgh_size = sizeof(msg),
30 },
31 };
32
33 kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
34 msg.header.msgh_size, 0, MACH_PORT_NULL, 10000, 0);
35
36 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client mach_msg");
37 }
38
39 static kern_return_t
receive(mach_port_t rcv_port)40 receive(mach_port_t rcv_port)
41 {
42 mach_msg_base_t msg = {
43 .header = {
44 .msgh_remote_port = MACH_PORT_NULL,
45 .msgh_local_port = rcv_port,
46 .msgh_size = sizeof(msg),
47 },
48 };
49
50 return mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT,
51 0, msg.header.msgh_size, rcv_port, 5000, 0);
52 }
53
54 static void
fill_kevent(struct kevent * ke,uint16_t action,mach_port_t mp)55 fill_kevent(struct kevent *ke, uint16_t action, mach_port_t mp)
56 {
57 *ke = (struct kevent){
58 .filter = EVFILT_MACHPORT,
59 .flags = action,
60 .ident = mp,
61 };
62 }
63
64 #define TS(s) (struct timespec){ .tv_sec = s }
65
66 static void *
pthread_async_do(void * arg)67 pthread_async_do(void *arg)
68 {
69 void (^block)(void) = arg;
70 block();
71 Block_release(block);
72 pthread_detach(pthread_self());
73 return NULL;
74 }
75
76 static void
77 pthread_async(void (^block)(void))
78 {
79 pthread_t th;
80 int rc;
81
82 rc = pthread_create(&th, NULL, pthread_async_do, Block_copy(block));
83 T_QUIET; T_ASSERT_POSIX_SUCCESS(rc, "pthread_create");
84 }
85
86 T_DECL(kqueue_machport, "basic EVFILT_MACHPORT tests")
87 {
88 mach_port_options_t opts = {
89 .flags = MPO_INSERT_SEND_RIGHT,
90 };
91 mach_port_t mp, pset;
92 kern_return_t kr;
93 struct kevent ke[2];
94 int kq, rc;
95
96 kr = mach_port_construct(mach_task_self(), &opts, 0, &mp);
97 T_EXPECT_MACH_SUCCESS(kr, "mach_port_construct()");
98
99 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &pset);
100 T_EXPECT_MACH_SUCCESS(kr, "mach_port_allocate(PSET)");
101
102 kr = mach_port_move_member(mach_task_self(), mp, pset);
103 T_EXPECT_MACH_SUCCESS(kr, "mach_port_move_member(PORT, PSET)");
104
105 kq = kqueue();
106 T_EXPECT_POSIX_SUCCESS(kq, "kqueue()");
107
108 /*
109 * Fired when attached
110 */
111 send(mp);
112
113 fill_kevent(&ke[0], EV_ADD, mp);
114 fill_kevent(&ke[1], EV_ADD, pset);
115 rc = kevent(kq, ke, 2, NULL, 0, &TS(5));
116 T_EXPECT_POSIX_SUCCESS(rc, "kevent(registration)");
117
118 rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
119 T_EXPECT_EQ(rc, 2, "kevent(fired at attach time)");
120
121 receive(mp);
122 rc = kevent(kq, NULL, 0, ke, 2, &TS(1));
123 T_EXPECT_EQ(rc, 0, "no event");
124
125 /*
126 * Fired after being attached, before wait
127 */
128 send(mp);
129 rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
130 T_EXPECT_EQ(rc, 2, "kevent(fired after attach time, before wait)");
131
132 receive(mp);
133 rc = kevent(kq, NULL, 0, ke, 2, &TS(1));
134 T_EXPECT_EQ(rc, 0, "no event");
135
136 /*
137 * Fired after being attached, after wait
138 */
139 pthread_async(^{
140 sleep(1);
141 send(mp);
142 });
143 rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
144 T_EXPECT_EQ(rc, 2, "kevent(fired after attach time, after wait)");
145
146 receive(mp);
147 rc = kevent(kq, NULL, 0, ke, 2, &TS(1));
148 T_EXPECT_EQ(rc, 0, "no event");
149
150 /* Make sure destroying ports wakes you up */
151 pthread_async(^{
152 sleep(1);
153 T_EXPECT_MACH_SUCCESS(mach_port_destruct(mach_task_self(), mp, -1, 0),
154 "mach_port_destruct");
155 });
156 rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
157 T_EXPECT_EQ(rc, 1, "kevent(port-destroyed)");
158 T_EXPECT_EQ(ke[0].ident, (uintptr_t)mp, "event was for the port");
159
160 pthread_async(^{
161 sleep(1);
162 T_EXPECT_MACH_SUCCESS(mach_port_mod_refs(mach_task_self(), pset,
163 MACH_PORT_RIGHT_PORT_SET, -1), "destroy pset");
164 });
165 rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
166 T_EXPECT_EQ(rc, 1, "kevent(port-destroyed)");
167 T_EXPECT_EQ(ke[0].ident, (uintptr_t)pset, "event was for the pset");
168 }
169