ares_event_kqueue.c (6751B)
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 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 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 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 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 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 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 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 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