1 /* MIT License
2 *
3 * Copyright (c) 2024 Brad House
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26 #include "ares_private.h"
27 #include "ares_event.h"
28
29 #if defined(HAVE_KQUEUE) && defined(CARES_THREADS)
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34 #ifdef HAVE_SYS_EVENT_H
35 # include <sys/event.h>
36 #endif
37 #ifdef HAVE_SYS_TIME_H
38 # include <sys/time.h>
39 #endif
40 #ifdef HAVE_FCNTL_H
41 # include <fcntl.h>
42 #endif
43
44 typedef struct {
45 int kqueue_fd;
46 struct kevent *changelist;
47 size_t nchanges;
48 size_t nchanges_alloc;
49 } ares_evsys_kqueue_t;
50
ares_evsys_kqueue_destroy(ares_event_thread_t * e)51 static void ares_evsys_kqueue_destroy(ares_event_thread_t *e)
52 {
53 ares_evsys_kqueue_t *kq = NULL;
54
55 if (e == NULL) {
56 return;
57 }
58
59 kq = e->ev_sys_data;
60 if (kq == NULL) {
61 return;
62 }
63
64 if (kq->kqueue_fd != -1) {
65 close(kq->kqueue_fd);
66 }
67
68 ares_free(kq->changelist);
69 ares_free(kq);
70 e->ev_sys_data = NULL;
71 }
72
ares_evsys_kqueue_init(ares_event_thread_t * e)73 static ares_bool_t ares_evsys_kqueue_init(ares_event_thread_t *e)
74 {
75 ares_evsys_kqueue_t *kq = NULL;
76
77 kq = ares_malloc_zero(sizeof(*kq));
78 if (kq == NULL) {
79 return ARES_FALSE;
80 }
81
82 e->ev_sys_data = kq;
83
84 kq->kqueue_fd = kqueue();
85 if (kq->kqueue_fd == -1) {
86 ares_evsys_kqueue_destroy(e);
87 return ARES_FALSE;
88 }
89
90 # ifdef FD_CLOEXEC
91 fcntl(kq->kqueue_fd, F_SETFD, FD_CLOEXEC);
92 # endif
93
94 kq->nchanges_alloc = 8;
95 kq->changelist =
96 ares_malloc_zero(kq->nchanges_alloc * sizeof(*kq->changelist));
97 if (kq->changelist == NULL) {
98 ares_evsys_kqueue_destroy(e);
99 return ARES_FALSE;
100 }
101
102 e->ev_signal = ares_pipeevent_create(e);
103 if (e->ev_signal == NULL) {
104 ares_evsys_kqueue_destroy(e);
105 return ARES_FALSE;
106 }
107
108 return ARES_TRUE;
109 }
110
ares_evsys_kqueue_enqueue(ares_evsys_kqueue_t * kq,int fd,int16_t filter,uint16_t flags)111 static void ares_evsys_kqueue_enqueue(ares_evsys_kqueue_t *kq, int fd,
112 int16_t filter, uint16_t flags)
113 {
114 size_t idx;
115
116 if (kq == NULL) {
117 return;
118 }
119
120 idx = kq->nchanges;
121
122 kq->nchanges++;
123
124 if (kq->nchanges > kq->nchanges_alloc) {
125 kq->nchanges_alloc <<= 1;
126 kq->changelist = ares_realloc_zero(
127 kq->changelist, (kq->nchanges_alloc >> 1) * sizeof(*kq->changelist),
128 kq->nchanges_alloc * sizeof(*kq->changelist));
129 }
130
131 EV_SET(&kq->changelist[idx], fd, filter, flags, 0, 0, 0);
132 }
133
ares_evsys_kqueue_event_process(ares_event_t * event,ares_event_flags_t old_flags,ares_event_flags_t new_flags)134 static void ares_evsys_kqueue_event_process(ares_event_t *event,
135 ares_event_flags_t old_flags,
136 ares_event_flags_t new_flags)
137 {
138 ares_event_thread_t *e = event->e;
139 ares_evsys_kqueue_t *kq;
140
141 if (e == NULL) {
142 return;
143 }
144
145 kq = e->ev_sys_data;
146 if (kq == NULL) {
147 return;
148 }
149
150 if (new_flags & ARES_EVENT_FLAG_READ && !(old_flags & ARES_EVENT_FLAG_READ)) {
151 ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_READ, EV_ADD | EV_ENABLE);
152 }
153
154 if (!(new_flags & ARES_EVENT_FLAG_READ) && old_flags & ARES_EVENT_FLAG_READ) {
155 ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_READ, EV_DELETE);
156 }
157
158 if (new_flags & ARES_EVENT_FLAG_WRITE &&
159 !(old_flags & ARES_EVENT_FLAG_WRITE)) {
160 ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_WRITE, EV_ADD | EV_ENABLE);
161 }
162
163 if (!(new_flags & ARES_EVENT_FLAG_WRITE) &&
164 old_flags & ARES_EVENT_FLAG_WRITE) {
165 ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_WRITE, EV_DELETE);
166 }
167 }
168
ares_evsys_kqueue_event_add(ares_event_t * event)169 static ares_bool_t ares_evsys_kqueue_event_add(ares_event_t *event)
170 {
171 ares_evsys_kqueue_event_process(event, 0, event->flags);
172 return ARES_TRUE;
173 }
174
ares_evsys_kqueue_event_del(ares_event_t * event)175 static void ares_evsys_kqueue_event_del(ares_event_t *event)
176 {
177 ares_evsys_kqueue_event_process(event, event->flags, 0);
178 }
179
ares_evsys_kqueue_event_mod(ares_event_t * event,ares_event_flags_t new_flags)180 static void ares_evsys_kqueue_event_mod(ares_event_t *event,
181 ares_event_flags_t new_flags)
182 {
183 ares_evsys_kqueue_event_process(event, event->flags, new_flags);
184 }
185
ares_evsys_kqueue_wait(ares_event_thread_t * e,unsigned long timeout_ms)186 static size_t ares_evsys_kqueue_wait(ares_event_thread_t *e,
187 unsigned long timeout_ms)
188 {
189 struct kevent events[8];
190 size_t nevents = sizeof(events) / sizeof(*events);
191 ares_evsys_kqueue_t *kq = e->ev_sys_data;
192 int rv;
193 size_t i;
194 struct timespec ts;
195 struct timespec *timeout = NULL;
196 size_t cnt = 0;
197
198 if (timeout_ms != 0) {
199 ts.tv_sec = (time_t)timeout_ms / 1000;
200 ts.tv_nsec = (timeout_ms % 1000) * 1000 * 1000;
201 timeout = &ts;
202 }
203
204 memset(events, 0, sizeof(events));
205
206 rv = kevent(kq->kqueue_fd, kq->changelist, (int)kq->nchanges, events,
207 (int)nevents, timeout);
208 if (rv < 0) {
209 return 0;
210 }
211
212 /* Changelist was consumed */
213 kq->nchanges = 0;
214 nevents = (size_t)rv;
215
216 for (i = 0; i < nevents; i++) {
217 ares_event_t *ev;
218 ares_event_flags_t flags = 0;
219
220 ev = ares_htable_asvp_get_direct(e->ev_sock_handles,
221 (ares_socket_t)events[i].ident);
222 if (ev == NULL || ev->cb == NULL) {
223 continue;
224 }
225
226 cnt++;
227
228 if (events[i].filter == EVFILT_READ ||
229 events[i].flags & (EV_EOF | EV_ERROR)) {
230 flags |= ARES_EVENT_FLAG_READ;
231 } else {
232 flags |= ARES_EVENT_FLAG_WRITE;
233 }
234
235 ev->cb(e, ev->fd, ev->data, flags);
236 }
237
238 return cnt;
239 }
240
241 const ares_event_sys_t ares_evsys_kqueue = { "kqueue",
242 ares_evsys_kqueue_init,
243 ares_evsys_kqueue_destroy,
244 ares_evsys_kqueue_event_add,
245 ares_evsys_kqueue_event_del,
246 ares_evsys_kqueue_event_mod,
247 ares_evsys_kqueue_wait };
248 #endif
249