ares_event_epoll.c (5806B)
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 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 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 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 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 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 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