• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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