• 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_EPOLL) && defined(CARES_THREADS)
30 
31 #ifdef HAVE_SYS_EPOLL_H
32 #  include <sys/epoll.h>
33 #endif
34 #ifdef HAVE_FCNTL_H
35 #  include <fcntl.h>
36 #endif
37 
38 typedef struct {
39   int epoll_fd;
40 } ares_evsys_epoll_t;
41 
ares_evsys_epoll_destroy(ares_event_thread_t * e)42 static void ares_evsys_epoll_destroy(ares_event_thread_t *e)
43 {
44   ares_evsys_epoll_t *ep = NULL;
45 
46   if (e == NULL) {
47     return; /* LCOV_EXCL_LINE: DefensiveCoding */
48   }
49 
50   ep = e->ev_sys_data;
51   if (ep == NULL) {
52     return; /* LCOV_EXCL_LINE: DefensiveCoding */
53   }
54 
55   if (ep->epoll_fd != -1) {
56     close(ep->epoll_fd);
57   }
58 
59   ares_free(ep);
60   e->ev_sys_data = NULL;
61 }
62 
ares_evsys_epoll_init(ares_event_thread_t * e)63 static ares_bool_t ares_evsys_epoll_init(ares_event_thread_t *e)
64 {
65   ares_evsys_epoll_t *ep = NULL;
66 
67   ep = ares_malloc_zero(sizeof(*ep));
68   if (ep == NULL) {
69     return ARES_FALSE; /* LCOV_EXCL_LINE: OutOfMemory */
70   }
71 
72   e->ev_sys_data = ep;
73 
74   ep->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
75   if (ep->epoll_fd == -1) {
76     ares_evsys_epoll_destroy(e); /* LCOV_EXCL_LINE: UntestablePath */
77     return ARES_FALSE;           /* LCOV_EXCL_LINE: UntestablePath */
78   }
79 
80   e->ev_signal = ares_pipeevent_create(e);
81   if (e->ev_signal == NULL) {
82     ares_evsys_epoll_destroy(e); /* LCOV_EXCL_LINE: UntestablePath */
83     return ARES_FALSE;           /* LCOV_EXCL_LINE: UntestablePath */
84   }
85 
86   return ARES_TRUE;
87 }
88 
ares_evsys_epoll_event_add(ares_event_t * event)89 static ares_bool_t ares_evsys_epoll_event_add(ares_event_t *event)
90 {
91   const ares_event_thread_t *e  = event->e;
92   const ares_evsys_epoll_t  *ep = e->ev_sys_data;
93   struct epoll_event         epev;
94 
95   memset(&epev, 0, sizeof(epev));
96   epev.data.fd = event->fd;
97   epev.events  = EPOLLRDHUP | EPOLLERR | EPOLLHUP;
98   if (event->flags & ARES_EVENT_FLAG_READ) {
99     epev.events |= EPOLLIN;
100   }
101   if (event->flags & ARES_EVENT_FLAG_WRITE) {
102     epev.events |= EPOLLOUT;
103   }
104   if (epoll_ctl(ep->epoll_fd, EPOLL_CTL_ADD, event->fd, &epev) != 0) {
105     return ARES_FALSE; /* LCOV_EXCL_LINE: UntestablePath */
106   }
107   return ARES_TRUE;
108 }
109 
ares_evsys_epoll_event_del(ares_event_t * event)110 static void ares_evsys_epoll_event_del(ares_event_t *event)
111 {
112   const ares_event_thread_t *e  = event->e;
113   const ares_evsys_epoll_t  *ep = e->ev_sys_data;
114   struct epoll_event         epev;
115 
116   memset(&epev, 0, sizeof(epev));
117   epev.data.fd = event->fd;
118   epoll_ctl(ep->epoll_fd, EPOLL_CTL_DEL, event->fd, &epev);
119 }
120 
ares_evsys_epoll_event_mod(ares_event_t * event,ares_event_flags_t new_flags)121 static void ares_evsys_epoll_event_mod(ares_event_t      *event,
122                                        ares_event_flags_t new_flags)
123 {
124   const ares_event_thread_t *e  = event->e;
125   const ares_evsys_epoll_t  *ep = e->ev_sys_data;
126   struct epoll_event         epev;
127 
128   memset(&epev, 0, sizeof(epev));
129   epev.data.fd = event->fd;
130   epev.events  = EPOLLRDHUP | EPOLLERR | EPOLLHUP;
131   if (new_flags & ARES_EVENT_FLAG_READ) {
132     epev.events |= EPOLLIN;
133   }
134   if (new_flags & ARES_EVENT_FLAG_WRITE) {
135     epev.events |= EPOLLOUT;
136   }
137   epoll_ctl(ep->epoll_fd, EPOLL_CTL_MOD, event->fd, &epev);
138 }
139 
ares_evsys_epoll_wait(ares_event_thread_t * e,unsigned long timeout_ms)140 static size_t ares_evsys_epoll_wait(ares_event_thread_t *e,
141                                     unsigned long        timeout_ms)
142 {
143   struct epoll_event        events[8];
144   size_t                    nevents = sizeof(events) / sizeof(*events);
145   const ares_evsys_epoll_t *ep      = e->ev_sys_data;
146   int                       rv;
147   size_t                    i;
148   size_t                    cnt = 0;
149 
150   memset(events, 0, sizeof(events));
151 
152   rv = epoll_wait(ep->epoll_fd, events, (int)nevents,
153                   (timeout_ms == 0) ? -1 : (int)timeout_ms);
154   if (rv < 0) {
155     return 0; /* LCOV_EXCL_LINE: UntestablePath */
156   }
157 
158   nevents = (size_t)rv;
159 
160   for (i = 0; i < nevents; i++) {
161     ares_event_t      *ev;
162     ares_event_flags_t flags = 0;
163 
164     ev = ares_htable_asvp_get_direct(e->ev_sock_handles,
165                                      (ares_socket_t)events[i].data.fd);
166     if (ev == NULL || ev->cb == NULL) {
167       continue; /* LCOV_EXCL_LINE: DefensiveCoding */
168     }
169 
170     cnt++;
171 
172     if (events[i].events & (EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
173       flags |= ARES_EVENT_FLAG_READ;
174     }
175     if (events[i].events & EPOLLOUT) {
176       flags |= ARES_EVENT_FLAG_WRITE;
177     }
178 
179     ev->cb(e, ev->fd, ev->data, flags);
180   }
181 
182   return cnt;
183 }
184 
185 const ares_event_sys_t ares_evsys_epoll = { "epoll",
186                                             ares_evsys_epoll_init,
187                                             ares_evsys_epoll_destroy,
188                                             ares_evsys_epoll_event_add,
189                                             ares_evsys_epoll_event_del,
190                                             ares_evsys_epoll_event_mod,
191                                             ares_evsys_epoll_wait };
192 #endif
193