mirror of https://github.com/c-ares/c-ares.git
Event Subsystem: No longer require integrators to have their own (#696)
This PR implements an event thread to process all events on file descriptors registered by c-ares. Prior to this feature, integrators were required to understand the internals of c-ares and how to monitor file descriptors and timeouts and process events. Implements OS-specific efficient polling such as epoll(), kqueue(), or IOCP, and falls back to poll() or select() if otherwise unsupported. At this point, it depends on basic threading primitives such as pthreads or windows threads. If enabled via the ARES_OPT_EVENT_THREAD option passed to ares_init_options(), then socket callbacks cannot be used. Fixes Bug: #611 Fix By: Brad House (@bradh352)pull/698/head
parent
26642c1014
commit
7963c519fc
44 changed files with 4550 additions and 178 deletions
@ -0,0 +1,45 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2023 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#ifndef __ARES__THREADS_H |
||||
#define __ARES__THREADS_H |
||||
|
||||
struct ares__thread_mutex; |
||||
typedef struct ares__thread_mutex ares__thread_mutex_t; |
||||
|
||||
ares__thread_mutex_t *ares__thread_mutex_create(void); |
||||
void ares__thread_mutex_destroy(ares__thread_mutex_t *mut); |
||||
void ares__thread_mutex_lock(ares__thread_mutex_t *mut); |
||||
void ares__thread_mutex_unlock(ares__thread_mutex_t *mut); |
||||
|
||||
struct ares__thread; |
||||
typedef struct ares__thread ares__thread_t; |
||||
|
||||
typedef void *(*ares__thread_func_t)(void *arg); |
||||
ares_status_t ares__thread_create(ares__thread_t **thread, |
||||
ares__thread_func_t func, void *arg); |
||||
ares_status_t ares__thread_join(ares__thread_t *thread, void **rv); |
||||
|
||||
#endif |
@ -0,0 +1,174 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2023 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#ifndef __ARES__EVENT_H |
||||
#define __ARES__EVENT_H |
||||
|
||||
#include "ares_setup.h" |
||||
|
||||
struct ares_event; |
||||
typedef struct ares_event ares_event_t; |
||||
|
||||
typedef enum { |
||||
ARES_EVENT_FLAG_NONE = 0, |
||||
ARES_EVENT_FLAG_READ = 1 << 0, |
||||
ARES_EVENT_FLAG_WRITE = 1 << 1, |
||||
ARES_EVENT_FLAG_OTHER = 1 << 2 |
||||
} ares_event_flags_t; |
||||
|
||||
typedef void (*ares_event_cb_t)(ares_event_thread_t *e, ares_socket_t fd, |
||||
void *data, ares_event_flags_t flags); |
||||
|
||||
typedef void (*ares_event_free_data_t)(void *data); |
||||
|
||||
typedef void (*ares_event_signal_cb_t)(const ares_event_t *event); |
||||
|
||||
struct ares_event { |
||||
/*! Registered event thread this event is bound to */ |
||||
ares_event_thread_t *e; |
||||
/*! Flags to monitor. OTHER is only allowed if the socket is ARES_SOCKET_BAD.
|
||||
*/ |
||||
ares_event_flags_t flags; |
||||
/*! Callback to be called when event is triggered */ |
||||
ares_event_cb_t cb; |
||||
/*! Socket to monitor, allowed to be ARES_SOCKET_BAD if not monitoring a
|
||||
* socket. */ |
||||
ares_socket_t fd; |
||||
/*! Data associated with event handle that will be passed to the callback.
|
||||
* Typically OS/event subsystem specific data. |
||||
* Optional, may be NULL. */ |
||||
/*! Data to be passed to callback. Optional, may be NULL. */ |
||||
void *data; |
||||
/*! When cleaning up the registered event (either when removed or during
|
||||
* shutdown), this function will be called to clean up the user-supplied |
||||
* data. Optional, May be NULL. */ |
||||
ares_event_free_data_t free_data_cb; |
||||
/*! Callback to call to trigger an event. */ |
||||
ares_event_signal_cb_t signal_cb; |
||||
}; |
||||
|
||||
typedef struct { |
||||
const char *name; |
||||
ares_bool_t (*init)(ares_event_thread_t *e); |
||||
void (*destroy)(ares_event_thread_t *e); |
||||
ares_bool_t (*event_add)(ares_event_t *event); |
||||
void (*event_del)(ares_event_t *event); |
||||
void (*event_mod)(ares_event_t *event, ares_event_flags_t new_flags); |
||||
size_t (*wait)(ares_event_thread_t *e, unsigned long timeout_ms); |
||||
} ares_event_sys_t; |
||||
|
||||
struct ares_event_thread { |
||||
/*! Whether the event thread should be online or not. Checked on every wake
|
||||
* event before sleeping. */ |
||||
ares_bool_t isup; |
||||
/*! Handle to the thread for joining during shutdown */ |
||||
ares__thread_t *thread; |
||||
/*! Lock to protect the data contained within the event thread itself */ |
||||
ares__thread_mutex_t *mutex; |
||||
/*! Reference to the ares channel, for being able to call things like
|
||||
* ares_timeout() and ares_process_fd(). */ |
||||
ares_channel_t *channel; |
||||
/*! Not-yet-processed event handle updates. These will get enqueued by a
|
||||
* thread other than the event thread itself. The event thread will then |
||||
* be woken then process these updates itself */ |
||||
ares__llist_t *ev_updates; |
||||
/*! Registered event handles. */ |
||||
ares__htable_asvp_t *ev_handles; |
||||
/*! Pointer to the event handle which is used to signal and wake the event
|
||||
* thread itself. This is needed to be able to do things like update the |
||||
* file descriptors being waited on and to wake the event subsystem during |
||||
* shutdown */ |
||||
ares_event_t *ev_signal; |
||||
/* Event subsystem callbacks */ |
||||
const ares_event_sys_t *ev_sys; |
||||
/* Event subsystem private data */ |
||||
void *ev_sys_data; |
||||
}; |
||||
|
||||
/*! Queue an update for the event handle.
|
||||
* |
||||
* Will search by the fd passed if not ARES_SOCKET_BAD to find a match and |
||||
* perform an update or delete (depending on flags). Otherwise will add. |
||||
* Do not use the event handle returned if its not guaranteed to be an add |
||||
* operation. |
||||
* |
||||
* \param[out] event Event handle. Optional, can be NULL. This handle |
||||
* will be invalidate quickly if the result of the |
||||
* operation is not an ADD. |
||||
* \param[in] e pointer to event thread handle |
||||
* \param[in] flags flags for the event handle. Use |
||||
* ARES_EVENT_FLAG_NONE if removing a socket from |
||||
* queue (not valid if socket is ARES_SOCKET_BAD). |
||||
* Non-socket events cannot be removed, and must have |
||||
* ARES_EVENT_FLAG_OTHER set. |
||||
* \param[in] cb Callback to call when |
||||
* event is triggered. Required. Not allowed to be |
||||
* changed, ignored on modification. |
||||
* \param[in] fd File descriptor/socket to monitor. May |
||||
* be ARES_SOCKET_BAD if not monitoring file |
||||
* descriptor. |
||||
* \param[in] data Optional. Caller-supplied data to be passed to |
||||
* callback. Only allowed on initial add, cannot be |
||||
* modified later, ignored on modification. |
||||
* \param[in] free_data_cb Optional. Callback to clean up caller-supplied |
||||
* data. Only allowed on initial add, cannot be |
||||
* modified later, ignored on modification. |
||||
* \param[in] signal_cb Optional. Callback to call to trigger an event. |
||||
* \return ARES_SUCCESS on success |
||||
*/ |
||||
ares_status_t ares_event_update(ares_event_t **event, ares_event_thread_t *e, |
||||
ares_event_flags_t flags, ares_event_cb_t cb, |
||||
ares_socket_t fd, void *data, |
||||
ares_event_free_data_t free_data_cb, |
||||
ares_event_signal_cb_t signal_cb); |
||||
|
||||
|
||||
#ifdef HAVE_PIPE |
||||
ares_event_t *ares_pipeevent_create(ares_event_thread_t *e); |
||||
#endif |
||||
|
||||
#ifdef HAVE_POLL |
||||
extern const ares_event_sys_t ares_evsys_poll; |
||||
#endif |
||||
|
||||
#ifdef HAVE_KQUEUE |
||||
extern const ares_event_sys_t ares_evsys_kqueue; |
||||
#endif |
||||
|
||||
#ifdef HAVE_EPOLL |
||||
extern const ares_event_sys_t ares_evsys_epoll; |
||||
#endif |
||||
|
||||
#ifdef _WIN32 |
||||
extern const ares_event_sys_t ares_evsys_win32; |
||||
#endif |
||||
|
||||
/* All systems have select(), but not all have a way to wake, so we require
|
||||
* pipe() to wake the select() */ |
||||
#ifdef HAVE_PIPE |
||||
extern const ares_event_sys_t ares_evsys_select; |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,198 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#include "ares_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
#include "ares_event.h" |
||||
|
||||
#ifdef HAVE_SYS_EPOLL_H |
||||
# include <sys/epoll.h> |
||||
#endif |
||||
#ifdef HAVE_FCNTL_H |
||||
# include <fcntl.h> |
||||
#endif |
||||
|
||||
#ifdef HAVE_EPOLL |
||||
|
||||
typedef struct { |
||||
int epoll_fd; |
||||
} ares_evsys_epoll_t; |
||||
|
||||
static void ares_evsys_epoll_destroy(ares_event_thread_t *e) |
||||
{ |
||||
ares_evsys_epoll_t *ep = NULL; |
||||
|
||||
if (e == NULL) { |
||||
return; |
||||
} |
||||
|
||||
ep = e->ev_sys_data; |
||||
if (ep == NULL) { |
||||
return; |
||||
} |
||||
|
||||
if (ep->epoll_fd != -1) { |
||||
close(ep->epoll_fd); |
||||
} |
||||
|
||||
ares_free(ep); |
||||
e->ev_sys_data = NULL; |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_epoll_init(ares_event_thread_t *e) |
||||
{ |
||||
ares_evsys_epoll_t *ep = NULL; |
||||
|
||||
ep = ares_malloc_zero(sizeof(*ep)); |
||||
if (ep == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
e->ev_sys_data = ep; |
||||
|
||||
ep->epoll_fd = epoll_create1(0); |
||||
if (ep->epoll_fd == -1) { |
||||
ares_evsys_epoll_destroy(e); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
# ifdef FD_CLOEXEC |
||||
fcntl(ep->epoll_fd, F_SETFD, FD_CLOEXEC); |
||||
# endif |
||||
|
||||
e->ev_signal = ares_pipeevent_create(e); |
||||
if (e->ev_signal == NULL) { |
||||
ares_evsys_epoll_destroy(e); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_epoll_event_add(ares_event_t *event) |
||||
{ |
||||
ares_event_thread_t *e = event->e; |
||||
ares_evsys_epoll_t *ep = e->ev_sys_data; |
||||
struct epoll_event epev; |
||||
|
||||
memset(&epev, 0, sizeof(epev)); |
||||
epev.data.fd = event->fd; |
||||
epev.events = EPOLLRDHUP | EPOLLERR | EPOLLHUP; |
||||
if (event->flags & ARES_EVENT_FLAG_READ) { |
||||
epev.events |= EPOLLIN; |
||||
} |
||||
if (event->flags & ARES_EVENT_FLAG_WRITE) { |
||||
epev.events |= EPOLLOUT; |
||||
} |
||||
if (epoll_ctl(ep->epoll_fd, EPOLL_CTL_ADD, event->fd, &epev) != 0) { |
||||
return ARES_FALSE; |
||||
} |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static void ares_evsys_epoll_event_del(ares_event_t *event) |
||||
{ |
||||
ares_event_thread_t *e = event->e; |
||||
ares_evsys_epoll_t *ep = e->ev_sys_data; |
||||
struct epoll_event epev; |
||||
|
||||
memset(&epev, 0, sizeof(epev)); |
||||
epev.data.fd = event->fd; |
||||
epoll_ctl(ep->epoll_fd, EPOLL_CTL_DEL, event->fd, &epev); |
||||
} |
||||
|
||||
static void ares_evsys_epoll_event_mod(ares_event_t *event, |
||||
ares_event_flags_t new_flags) |
||||
{ |
||||
ares_event_thread_t *e = event->e; |
||||
ares_evsys_epoll_t *ep = e->ev_sys_data; |
||||
struct epoll_event epev; |
||||
|
||||
memset(&epev, 0, sizeof(epev)); |
||||
epev.data.fd = event->fd; |
||||
epev.events = EPOLLRDHUP | EPOLLERR | EPOLLHUP; |
||||
if (new_flags & ARES_EVENT_FLAG_READ) { |
||||
epev.events |= EPOLLIN; |
||||
} |
||||
if (new_flags & ARES_EVENT_FLAG_WRITE) { |
||||
epev.events |= EPOLLOUT; |
||||
} |
||||
epoll_ctl(ep->epoll_fd, EPOLL_CTL_MOD, event->fd, &epev); |
||||
} |
||||
|
||||
static size_t ares_evsys_epoll_wait(ares_event_thread_t *e, |
||||
unsigned long timeout_ms) |
||||
{ |
||||
struct epoll_event events[8]; |
||||
size_t nevents = sizeof(events) / sizeof(*events); |
||||
ares_evsys_epoll_t *ep = e->ev_sys_data; |
||||
int rv; |
||||
size_t i; |
||||
size_t cnt = 0; |
||||
|
||||
memset(events, 0, sizeof(events)); |
||||
|
||||
rv = epoll_wait(ep->epoll_fd, events, (int)nevents, |
||||
(timeout_ms == 0) ? -1 : (int)timeout_ms); |
||||
if (rv < 0) { |
||||
return 0; |
||||
} |
||||
|
||||
nevents = (size_t)rv; |
||||
|
||||
for (i = 0; i < nevents; i++) { |
||||
ares_event_t *ev; |
||||
ares_event_flags_t flags = 0; |
||||
|
||||
ev = ares__htable_asvp_get_direct(e->ev_handles, |
||||
(ares_socket_t)events[i].data.fd); |
||||
if (ev == NULL || ev->cb == NULL) { |
||||
continue; |
||||
} |
||||
|
||||
cnt++; |
||||
|
||||
if (events[i].events & (EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR)) { |
||||
flags |= ARES_EVENT_FLAG_READ; |
||||
} |
||||
if (events[i].events & EPOLLOUT) { |
||||
flags |= ARES_EVENT_FLAG_WRITE; |
||||
} |
||||
|
||||
ev->cb(e, ev->fd, ev->data, flags); |
||||
} |
||||
|
||||
return cnt; |
||||
} |
||||
|
||||
const ares_event_sys_t ares_evsys_epoll = { "epoll", |
||||
ares_evsys_epoll_init, |
||||
ares_evsys_epoll_destroy, |
||||
ares_evsys_epoll_event_add, |
||||
ares_evsys_epoll_event_del, |
||||
ares_evsys_epoll_event_mod, |
||||
ares_evsys_epoll_wait }; |
||||
#endif |
@ -0,0 +1,249 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#include "ares_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
#include "ares_event.h" |
||||
|
||||
#ifdef HAVE_SYS_TYPES_H |
||||
# include <sys/types.h> |
||||
#endif |
||||
#ifdef HAVE_SYS_EVENT_H |
||||
# include <sys/event.h> |
||||
#endif |
||||
#ifdef HAVE_SYS_TIME_H |
||||
# include <sys/time.h> |
||||
#endif |
||||
#ifdef HAVE_FCNTL_H |
||||
# include <fcntl.h> |
||||
#endif |
||||
|
||||
#ifdef HAVE_KQUEUE |
||||
|
||||
typedef struct { |
||||
int kqueue_fd; |
||||
struct kevent *changelist; |
||||
size_t nchanges; |
||||
size_t nchanges_alloc; |
||||
} ares_evsys_kqueue_t; |
||||
|
||||
static void ares_evsys_kqueue_destroy(ares_event_thread_t *e) |
||||
{ |
||||
ares_evsys_kqueue_t *kq = NULL; |
||||
|
||||
if (e == NULL) { |
||||
return; |
||||
} |
||||
|
||||
kq = e->ev_sys_data; |
||||
if (kq == NULL) { |
||||
return; |
||||
} |
||||
|
||||
if (kq->kqueue_fd != -1) { |
||||
close(kq->kqueue_fd); |
||||
} |
||||
|
||||
ares_free(kq->changelist); |
||||
ares_free(kq); |
||||
e->ev_sys_data = NULL; |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_kqueue_init(ares_event_thread_t *e) |
||||
{ |
||||
ares_evsys_kqueue_t *kq = NULL; |
||||
|
||||
kq = ares_malloc_zero(sizeof(*kq)); |
||||
if (kq == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
e->ev_sys_data = kq; |
||||
|
||||
kq->kqueue_fd = kqueue(); |
||||
if (kq->kqueue_fd == -1) { |
||||
ares_evsys_kqueue_destroy(e); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
# ifdef FD_CLOEXEC |
||||
fcntl(kq->kqueue_fd, F_SETFD, FD_CLOEXEC); |
||||
# endif |
||||
|
||||
kq->nchanges_alloc = 8; |
||||
kq->changelist = |
||||
ares_malloc_zero(sizeof(*kq->changelist) * kq->nchanges_alloc); |
||||
if (kq->changelist == NULL) { |
||||
ares_evsys_kqueue_destroy(e); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
e->ev_signal = ares_pipeevent_create(e); |
||||
if (e->ev_signal == NULL) { |
||||
ares_evsys_kqueue_destroy(e); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static void ares_evsys_kqueue_enqueue(ares_evsys_kqueue_t *kq, int fd, |
||||
int16_t filter, uint16_t flags) |
||||
{ |
||||
size_t idx; |
||||
|
||||
if (kq == NULL) { |
||||
return; |
||||
} |
||||
|
||||
idx = kq->nchanges; |
||||
|
||||
kq->nchanges++; |
||||
|
||||
if (kq->nchanges > kq->nchanges_alloc) { |
||||
kq->nchanges_alloc <<= 1; |
||||
kq->changelist = ares_realloc_zero(kq->changelist, kq->nchanges_alloc >> 1, |
||||
kq->nchanges_alloc); |
||||
} |
||||
|
||||
EV_SET(&kq->changelist[idx], fd, filter, flags, 0, 0, 0); |
||||
} |
||||
|
||||
static void ares_evsys_kqueue_event_process(ares_event_t *event, |
||||
ares_event_flags_t old_flags, |
||||
ares_event_flags_t new_flags) |
||||
{ |
||||
ares_event_thread_t *e = event->e; |
||||
ares_evsys_kqueue_t *kq; |
||||
|
||||
if (e == NULL) { |
||||
return; |
||||
} |
||||
|
||||
kq = e->ev_sys_data; |
||||
if (kq == NULL) { |
||||
return; |
||||
} |
||||
|
||||
if (new_flags & ARES_EVENT_FLAG_READ && !(old_flags & ARES_EVENT_FLAG_READ)) { |
||||
ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_READ, EV_ADD | EV_ENABLE); |
||||
} |
||||
|
||||
if (!(new_flags & ARES_EVENT_FLAG_READ) && old_flags & ARES_EVENT_FLAG_READ) { |
||||
ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_READ, EV_DELETE); |
||||
} |
||||
|
||||
if (new_flags & ARES_EVENT_FLAG_WRITE && |
||||
!(old_flags & ARES_EVENT_FLAG_WRITE)) { |
||||
ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_WRITE, EV_ADD | EV_ENABLE); |
||||
} |
||||
|
||||
if (!(new_flags & ARES_EVENT_FLAG_WRITE) && |
||||
old_flags & ARES_EVENT_FLAG_WRITE) { |
||||
ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_WRITE, EV_DELETE); |
||||
} |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_kqueue_event_add(ares_event_t *event) |
||||
{ |
||||
ares_evsys_kqueue_event_process(event, 0, event->flags); |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static void ares_evsys_kqueue_event_del(ares_event_t *event) |
||||
{ |
||||
ares_evsys_kqueue_event_process(event, event->flags, 0); |
||||
} |
||||
|
||||
static void ares_evsys_kqueue_event_mod(ares_event_t *event, |
||||
ares_event_flags_t new_flags) |
||||
{ |
||||
ares_evsys_kqueue_event_process(event, event->flags, new_flags); |
||||
} |
||||
|
||||
static size_t ares_evsys_kqueue_wait(ares_event_thread_t *e, |
||||
unsigned long timeout_ms) |
||||
{ |
||||
struct kevent events[8]; |
||||
size_t nevents = sizeof(events) / sizeof(*events); |
||||
ares_evsys_kqueue_t *kq = e->ev_sys_data; |
||||
int rv; |
||||
size_t i; |
||||
struct timespec ts; |
||||
struct timespec *timeout = NULL; |
||||
size_t cnt = 0; |
||||
|
||||
if (timeout_ms != 0) { |
||||
ts.tv_sec = timeout_ms / 1000; |
||||
ts.tv_nsec = (timeout_ms % 1000) * 1000 * 1000; |
||||
timeout = &ts; |
||||
} |
||||
|
||||
memset(events, 0, sizeof(events)); |
||||
|
||||
rv = kevent(kq->kqueue_fd, kq->changelist, (int)kq->nchanges, events, |
||||
(int)nevents, timeout); |
||||
if (rv < 0) { |
||||
return 0; |
||||
} |
||||
|
||||
/* Changelist was consumed */ |
||||
kq->nchanges = 0; |
||||
nevents = (size_t)rv; |
||||
|
||||
for (i = 0; i < nevents; i++) { |
||||
ares_event_t *ev; |
||||
ares_event_flags_t flags = 0; |
||||
|
||||
ev = ares__htable_asvp_get_direct(e->ev_handles, |
||||
(ares_socket_t)events[i].ident); |
||||
if (ev == NULL || ev->cb == NULL) { |
||||
continue; |
||||
} |
||||
|
||||
cnt++; |
||||
|
||||
if (events[i].filter == EVFILT_READ || |
||||
events[i].flags & (EV_EOF | EV_ERROR)) { |
||||
flags |= ARES_EVENT_FLAG_READ; |
||||
} else { |
||||
flags |= ARES_EVENT_FLAG_WRITE; |
||||
} |
||||
|
||||
ev->cb(e, ev->fd, ev->data, flags); |
||||
} |
||||
|
||||
return cnt; |
||||
} |
||||
|
||||
const ares_event_sys_t ares_evsys_kqueue = { "kqueue", |
||||
ares_evsys_kqueue_init, |
||||
ares_evsys_kqueue_destroy, |
||||
ares_evsys_kqueue_event_add, |
||||
ares_evsys_kqueue_event_del, |
||||
ares_evsys_kqueue_event_mod, |
||||
ares_evsys_kqueue_wait }; |
||||
#endif |
@ -0,0 +1,137 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#include "ares_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
#include "ares_event.h" |
||||
#ifdef HAVE_POLL_H |
||||
# include <poll.h> |
||||
#endif |
||||
|
||||
#if defined(HAVE_POLL) |
||||
|
||||
static ares_bool_t ares_evsys_poll_init(ares_event_thread_t *e) |
||||
{ |
||||
e->ev_signal = ares_pipeevent_create(e); |
||||
if (e->ev_signal == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static void ares_evsys_poll_destroy(ares_event_thread_t *e) |
||||
{ |
||||
(void)e; |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_poll_event_add(ares_event_t *event) |
||||
{ |
||||
(void)event; |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static void ares_evsys_poll_event_del(ares_event_t *event) |
||||
{ |
||||
(void)event; |
||||
} |
||||
|
||||
static void ares_evsys_poll_event_mod(ares_event_t *event, |
||||
ares_event_flags_t new_flags) |
||||
{ |
||||
(void)event; |
||||
(void)new_flags; |
||||
} |
||||
|
||||
static size_t ares_evsys_poll_wait(ares_event_thread_t *e, |
||||
unsigned long timeout_ms) |
||||
{ |
||||
size_t num_fds = 0; |
||||
ares_socket_t *fdlist = ares__htable_asvp_keys(e->ev_handles, &num_fds); |
||||
struct pollfd *pollfd = NULL; |
||||
int rv; |
||||
size_t cnt = 0; |
||||
size_t i; |
||||
|
||||
if (num_fds) { |
||||
pollfd = ares_malloc_zero(sizeof(*pollfd) * num_fds); |
||||
for (i = 0; i < num_fds; i++) { |
||||
ares_event_t *ev = ares__htable_asvp_get_direct(e->ev_handles, fdlist[i]); |
||||
pollfd[i].fd = ev->fd; |
||||
if (ev->flags & ARES_EVENT_FLAG_READ) { |
||||
pollfd[i].events |= POLLIN; |
||||
} |
||||
if (ev->flags & ARES_EVENT_FLAG_WRITE) { |
||||
pollfd[i].events |= POLLOUT; |
||||
} |
||||
} |
||||
} |
||||
ares_free(fdlist); |
||||
|
||||
rv = poll(pollfd, (nfds_t)num_fds, (timeout_ms == 0) ? -1 : (int)timeout_ms); |
||||
if (rv <= 0) { |
||||
goto done; |
||||
} |
||||
|
||||
for (i = 0; i < num_fds; i++) { |
||||
ares_event_t *ev; |
||||
ares_event_flags_t flags = 0; |
||||
|
||||
if (pollfd[i].revents == 0) { |
||||
continue; |
||||
} |
||||
|
||||
cnt++; |
||||
|
||||
ev = ares__htable_asvp_get_direct(e->ev_handles, pollfd[i].fd); |
||||
if (ev == NULL || ev->cb == NULL) { |
||||
continue; |
||||
} |
||||
|
||||
if (pollfd[i].revents & (POLLERR | POLLHUP | POLLIN)) { |
||||
flags |= ARES_EVENT_FLAG_READ; |
||||
} |
||||
|
||||
if (pollfd[i].revents & POLLOUT) { |
||||
flags |= ARES_EVENT_FLAG_WRITE; |
||||
} |
||||
|
||||
ev->cb(e, pollfd[i].fd, ev->data, flags); |
||||
} |
||||
|
||||
done: |
||||
ares_free(pollfd); |
||||
return cnt; |
||||
} |
||||
|
||||
const ares_event_sys_t ares_evsys_poll = { "poll", |
||||
ares_evsys_poll_init, |
||||
ares_evsys_poll_destroy, /* NoOp */ |
||||
ares_evsys_poll_event_add, /* NoOp */ |
||||
ares_evsys_poll_event_del, /* NoOp */ |
||||
ares_evsys_poll_event_mod, /* NoOp */ |
||||
ares_evsys_poll_wait }; |
||||
|
||||
#endif |
@ -0,0 +1,150 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#include "ares_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
#include "ares_event.h" |
||||
#ifdef HAVE_SYS_SELECT_H |
||||
# include <sys/select.h> |
||||
#endif |
||||
|
||||
/* All systems have select(), but not all have a way to wake, so we require
|
||||
* pipe() to wake the select() */ |
||||
#if defined(HAVE_PIPE) |
||||
|
||||
static ares_bool_t ares_evsys_select_init(ares_event_thread_t *e) |
||||
{ |
||||
e->ev_signal = ares_pipeevent_create(e); |
||||
if (e->ev_signal == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static void ares_evsys_select_destroy(ares_event_thread_t *e) |
||||
{ |
||||
(void)e; |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_select_event_add(ares_event_t *event) |
||||
{ |
||||
(void)event; |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static void ares_evsys_select_event_del(ares_event_t *event) |
||||
{ |
||||
(void)event; |
||||
} |
||||
|
||||
static void ares_evsys_select_event_mod(ares_event_t *event, |
||||
ares_event_flags_t new_flags) |
||||
{ |
||||
(void)event; |
||||
(void)new_flags; |
||||
} |
||||
|
||||
static size_t ares_evsys_select_wait(ares_event_thread_t *e, |
||||
unsigned long timeout_ms) |
||||
{ |
||||
size_t num_fds = 0; |
||||
ares_socket_t *fdlist = ares__htable_asvp_keys(e->ev_handles, &num_fds); |
||||
int rv; |
||||
size_t cnt = 0; |
||||
size_t i; |
||||
fd_set read_fds; |
||||
fd_set write_fds; |
||||
int nfds = 0; |
||||
struct timeval tv; |
||||
struct timeval *tout = NULL; |
||||
|
||||
FD_ZERO(&read_fds); |
||||
FD_ZERO(&write_fds); |
||||
|
||||
for (i = 0; i < num_fds; i++) { |
||||
ares_event_t *ev = ares__htable_asvp_get_direct(e->ev_handles, fdlist[i]); |
||||
if (ev->flags & ARES_EVENT_FLAG_READ) { |
||||
FD_SET(ev->fd, &read_fds); |
||||
} |
||||
if (ev->flags & ARES_EVENT_FLAG_WRITE) { |
||||
FD_SET(ev->fd, &write_fds); |
||||
} |
||||
if (ev->fd + 1 > nfds) { |
||||
nfds = ev->fd + 1; |
||||
} |
||||
} |
||||
|
||||
if (timeout_ms) { |
||||
tv.tv_sec = (int)(timeout_ms / 1000); |
||||
tv.tv_usec = (int)((timeout_ms % 1000) * 1000); |
||||
tout = &tv; |
||||
} |
||||
|
||||
rv = select(nfds, &read_fds, &write_fds, NULL, tout); |
||||
if (rv > 0) { |
||||
for (i = 0; i < num_fds; i++) { |
||||
ares_event_t *ev; |
||||
ares_event_flags_t flags = 0; |
||||
|
||||
ev = ares__htable_asvp_get_direct(e->ev_handles, fdlist[i]); |
||||
if (ev == NULL || ev->cb == NULL) { |
||||
continue; |
||||
} |
||||
|
||||
if (FD_ISSET(fdlist[i], &read_fds)) { |
||||
flags |= ARES_EVENT_FLAG_READ; |
||||
} |
||||
|
||||
if (FD_ISSET(fdlist[i], &write_fds)) { |
||||
flags |= ARES_EVENT_FLAG_WRITE; |
||||
} |
||||
|
||||
if (flags == 0) { |
||||
continue; |
||||
} |
||||
|
||||
cnt++; |
||||
|
||||
ev->cb(e, fdlist[i], ev->data, flags); |
||||
} |
||||
} |
||||
|
||||
ares_free(fdlist); |
||||
|
||||
return cnt; |
||||
} |
||||
|
||||
const ares_event_sys_t ares_evsys_select = { |
||||
"select", |
||||
ares_evsys_select_init, |
||||
ares_evsys_select_destroy, /* NoOp */ |
||||
ares_evsys_select_event_add, /* NoOp */ |
||||
ares_evsys_select_event_del, /* NoOp */ |
||||
ares_evsys_select_event_mod, /* NoOp */ |
||||
ares_evsys_select_wait |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,447 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#include "ares_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
#include "ares_event.h" |
||||
|
||||
static void ares_event_destroy_cb(void *arg) |
||||
{ |
||||
ares_event_t *event = arg; |
||||
if (event == NULL) { |
||||
return; |
||||
} |
||||
|
||||
/* Unregister from the event thread if it was registered with one */ |
||||
if (event->e) { |
||||
ares_event_thread_t *e = event->e; |
||||
e->ev_sys->event_del(event); |
||||
event->e = NULL; |
||||
} |
||||
|
||||
if (event->free_data_cb && event->data) { |
||||
event->free_data_cb(event->data); |
||||
} |
||||
|
||||
ares_free(event); |
||||
} |
||||
|
||||
/* See if a pending update already exists. We don't want to enqueue multiple
|
||||
* updates for the same event handle. Right now this is O(n) based on number |
||||
* of updates already enqueued. In the future, it might make sense to make |
||||
* this O(1) with a hashtable. */ |
||||
static ares_event_t *ares_event_update_find(ares_event_thread_t *e, |
||||
ares_socket_t fd, void *data) |
||||
{ |
||||
ares__llist_node_t *node; |
||||
|
||||
for (node = ares__llist_node_first(e->ev_updates); node != NULL; |
||||
node = ares__llist_node_next(node)) { |
||||
ares_event_t *ev = ares__llist_node_val(node); |
||||
|
||||
if (fd != ARES_SOCKET_BAD && fd == ev->fd) { |
||||
return ev; |
||||
} |
||||
|
||||
if (fd == ARES_SOCKET_BAD && ev->fd == ARES_SOCKET_BAD && |
||||
data == ev->data) { |
||||
return ev; |
||||
} |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
ares_status_t ares_event_update(ares_event_t **event, ares_event_thread_t *e, |
||||
ares_event_flags_t flags, ares_event_cb_t cb, |
||||
ares_socket_t fd, void *data, |
||||
ares_event_free_data_t free_data_cb, |
||||
ares_event_signal_cb_t signal_cb) |
||||
{ |
||||
ares_event_t *ev = NULL; |
||||
|
||||
if (e == NULL || cb == NULL) { |
||||
return ARES_EFORMERR; |
||||
} |
||||
|
||||
if (event != NULL) { |
||||
*event = NULL; |
||||
} |
||||
|
||||
/* Validate flags */ |
||||
if (fd == ARES_SOCKET_BAD) { |
||||
if (flags & (ARES_EVENT_FLAG_READ | ARES_EVENT_FLAG_WRITE)) { |
||||
return ARES_EFORMERR; |
||||
} |
||||
if (!(flags & ARES_EVENT_FLAG_OTHER)) { |
||||
return ARES_EFORMERR; |
||||
} |
||||
} else { |
||||
if (flags & ARES_EVENT_FLAG_OTHER) { |
||||
return ARES_EFORMERR; |
||||
} |
||||
} |
||||
|
||||
/* That's all the validation we can really do */ |
||||
|
||||
/* See if we have a queued update already */ |
||||
ev = ares_event_update_find(e, fd, data); |
||||
if (ev == NULL) { |
||||
/* Allocate a new one */ |
||||
ev = ares_malloc_zero(sizeof(*ev)); |
||||
if (ev == NULL) { |
||||
return ARES_ENOMEM; |
||||
} |
||||
|
||||
if (ares__llist_insert_last(e->ev_updates, ev) == NULL) { |
||||
ares_free(ev); |
||||
return ARES_ENOMEM; |
||||
} |
||||
} |
||||
|
||||
ev->flags = flags; |
||||
ev->fd = fd; |
||||
if (ev->cb == NULL) { |
||||
ev->cb = cb; |
||||
} |
||||
if (ev->data == NULL) { |
||||
ev->data = data; |
||||
} |
||||
if (ev->free_data_cb == NULL) { |
||||
ev->free_data_cb = free_data_cb; |
||||
} |
||||
if (ev->signal_cb == NULL) { |
||||
ev->signal_cb = signal_cb; |
||||
} |
||||
|
||||
if (event != NULL) { |
||||
*event = ev; |
||||
} |
||||
|
||||
return ARES_SUCCESS; |
||||
} |
||||
|
||||
static void ares_event_signal(const ares_event_t *event) |
||||
{ |
||||
if (event == NULL || event->signal_cb == NULL) { |
||||
return; |
||||
} |
||||
event->signal_cb(event); |
||||
} |
||||
|
||||
static void ares_event_thread_wake(ares_event_thread_t *e) |
||||
{ |
||||
if (e == NULL) { |
||||
return; |
||||
} |
||||
|
||||
ares_event_signal(e->ev_signal); |
||||
} |
||||
|
||||
static void ares_event_thread_process_fd(ares_event_thread_t *e, |
||||
ares_socket_t fd, void *data, |
||||
ares_event_flags_t flags) |
||||
{ |
||||
(void)data; |
||||
|
||||
ares_process_fd(e->channel, |
||||
(flags & ARES_EVENT_FLAG_READ) ? fd : ARES_SOCKET_BAD, |
||||
(flags & ARES_EVENT_FLAG_WRITE) ? fd : ARES_SOCKET_BAD); |
||||
} |
||||
|
||||
static void ares_event_thread_sockstate_cb(void *data, ares_socket_t socket_fd, |
||||
int readable, int writable) |
||||
{ |
||||
ares_event_thread_t *e = data; |
||||
ares_event_flags_t flags = ARES_EVENT_FLAG_NONE; |
||||
|
||||
if (readable) { |
||||
flags |= ARES_EVENT_FLAG_READ; |
||||
} |
||||
|
||||
if (writable) { |
||||
flags |= ARES_EVENT_FLAG_WRITE; |
||||
} |
||||
|
||||
/* Update channel fd */ |
||||
ares__thread_mutex_lock(e->mutex); |
||||
|
||||
ares_event_update(NULL, e, flags, ares_event_thread_process_fd, socket_fd, |
||||
NULL, NULL, NULL); |
||||
|
||||
/* Wake the event thread so it properly enqueues any updates */ |
||||
ares_event_thread_wake(e); |
||||
|
||||
ares__thread_mutex_unlock(e->mutex); |
||||
} |
||||
|
||||
static void ares_event_process_updates(ares_event_thread_t *e) |
||||
{ |
||||
ares__llist_node_t *node; |
||||
|
||||
/* Iterate across all updates and apply to internal list, removing from update
|
||||
* list */ |
||||
while ((node = ares__llist_node_first(e->ev_updates)) != NULL) { |
||||
ares_event_t *newev = ares__llist_node_claim(node); |
||||
ares_event_t *oldev = |
||||
ares__htable_asvp_get_direct(e->ev_handles, newev->fd); |
||||
|
||||
/* Adding new */ |
||||
if (oldev == NULL) { |
||||
newev->e = e; |
||||
/* Don't try to add a new event if all flags are cleared, that's basically
|
||||
* someone trying to delete something already deleted. Also if it fails |
||||
* to add, cleanup. */ |
||||
if (newev->flags == ARES_EVENT_FLAG_NONE || |
||||
!e->ev_sys->event_add(newev)) { |
||||
newev->e = NULL; |
||||
ares_event_destroy_cb(newev); |
||||
} else { |
||||
ares__htable_asvp_insert(e->ev_handles, newev->fd, newev); |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
/* Removal request */ |
||||
if (newev->flags == ARES_EVENT_FLAG_NONE) { |
||||
/* the callback for the removal will call e->ev_sys->event_del(e, event)
|
||||
*/ |
||||
ares__htable_asvp_remove(e->ev_handles, newev->fd); |
||||
ares_free(newev); |
||||
continue; |
||||
} |
||||
|
||||
/* Modify request -- only flags can be changed */ |
||||
e->ev_sys->event_mod(oldev, newev->flags); |
||||
oldev->flags = newev->flags; |
||||
ares_free(newev); |
||||
} |
||||
} |
||||
|
||||
static void *ares_event_thread(void *arg) |
||||
{ |
||||
ares_event_thread_t *e = arg; |
||||
ares__thread_mutex_lock(e->mutex); |
||||
|
||||
while (e->isup) { |
||||
struct timeval tv; |
||||
struct timeval *tvout; |
||||
unsigned long timeout_ms = 0; /* 0 = unlimited */ |
||||
|
||||
tvout = ares_timeout(e->channel, NULL, &tv); |
||||
if (tvout != NULL) { |
||||
timeout_ms = |
||||
(unsigned long)((tvout->tv_sec * 1000) + (tvout->tv_usec / 1000) + 1); |
||||
} |
||||
|
||||
ares_event_process_updates(e); |
||||
|
||||
/* Don't hold a mutex while waiting on events */ |
||||
ares__thread_mutex_unlock(e->mutex); |
||||
e->ev_sys->wait(e, timeout_ms); |
||||
|
||||
/* Each iteration should do timeout processing */ |
||||
if (e->isup) { |
||||
ares_process_fd(e->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); |
||||
} |
||||
|
||||
/* Relock before we loop again */ |
||||
ares__thread_mutex_lock(e->mutex); |
||||
} |
||||
|
||||
ares__thread_mutex_unlock(e->mutex); |
||||
return NULL; |
||||
} |
||||
|
||||
static void ares_event_thread_destroy_int(ares_event_thread_t *e) |
||||
{ |
||||
ares__llist_node_t *node; |
||||
|
||||
/* Wake thread and tell it to shutdown if it exists */ |
||||
ares__thread_mutex_lock(e->mutex); |
||||
if (e->isup) { |
||||
e->isup = ARES_FALSE; |
||||
ares_event_thread_wake(e); |
||||
} |
||||
ares__thread_mutex_unlock(e->mutex); |
||||
|
||||
/* Wait for thread to shutdown */ |
||||
if (e->thread) { |
||||
ares__thread_join(e->thread, NULL); |
||||
e->thread = NULL; |
||||
} |
||||
|
||||
/* Manually free any updates that weren't processed */ |
||||
while ((node = ares__llist_node_first(e->ev_updates)) != NULL) { |
||||
ares_event_destroy_cb(ares__llist_node_claim(node)); |
||||
} |
||||
ares__llist_destroy(e->ev_updates); |
||||
e->ev_updates = NULL; |
||||
|
||||
ares__htable_asvp_destroy(e->ev_handles); |
||||
e->ev_handles = NULL; |
||||
|
||||
if (e->ev_sys->destroy) { |
||||
e->ev_sys->destroy(e); |
||||
} |
||||
|
||||
ares__thread_mutex_destroy(e->mutex); |
||||
e->mutex = NULL; |
||||
|
||||
ares_free(e); |
||||
} |
||||
|
||||
void ares_event_thread_destroy(ares_channel_t *channel) |
||||
{ |
||||
ares_event_thread_t *e = channel->sock_state_cb_data; |
||||
|
||||
if (e == NULL) { |
||||
return; |
||||
} |
||||
|
||||
ares_event_thread_destroy_int(e); |
||||
} |
||||
|
||||
static const ares_event_sys_t *ares_event_fetch_sys(ares_evsys_t evsys) |
||||
{ |
||||
switch (evsys) { |
||||
case ARES_EVSYS_WIN32: |
||||
#if defined(_WIN32) |
||||
return &ares_evsys_win32; |
||||
#else |
||||
return NULL; |
||||
#endif |
||||
|
||||
case ARES_EVSYS_EPOLL: |
||||
#if defined(HAVE_EPOLL) |
||||
return &ares_evsys_epoll; |
||||
#else |
||||
return NULL; |
||||
#endif |
||||
|
||||
case ARES_EVSYS_KQUEUE: |
||||
#if defined(HAVE_KQUEUE) |
||||
return &ares_evsys_kqueue; |
||||
#else |
||||
return NULL; |
||||
#endif |
||||
|
||||
case ARES_EVSYS_POLL: |
||||
#if defined(HAVE_POLL) |
||||
return &ares_evsys_poll; |
||||
#else |
||||
return NULL; |
||||
#endif |
||||
|
||||
case ARES_EVSYS_SELECT: |
||||
#if defined(HAVE_PIPE) |
||||
return &ares_evsys_select; |
||||
#else |
||||
return NULL; |
||||
#endif |
||||
|
||||
case ARES_EVSYS_DEFAULT: |
||||
default: |
||||
#if defined(_WIN32) |
||||
return &ares_evsys_win32; |
||||
#elif defined(HAVE_KQUEUE) |
||||
return &ares_evsys_kqueue; |
||||
#elif defined(HAVE_EPOLL) |
||||
return &ares_evsys_epoll; |
||||
#elif defined(HAVE_POLL) |
||||
return &ares_evsys_poll; |
||||
#elif defined(HAVE_PIPE) |
||||
return &ares_evsys_select; |
||||
#else |
||||
break; |
||||
#endif |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
ares_status_t ares_event_thread_init(ares_channel_t *channel) |
||||
{ |
||||
ares_event_thread_t *e; |
||||
|
||||
e = ares_malloc_zero(sizeof(*e)); |
||||
if (e == NULL) { |
||||
return ARES_ENOMEM; |
||||
} |
||||
|
||||
e->mutex = ares__thread_mutex_create(); |
||||
if (e->mutex == NULL) { |
||||
ares_event_thread_destroy_int(e); |
||||
return ARES_ENOMEM; |
||||
} |
||||
|
||||
e->ev_updates = ares__llist_create(NULL); |
||||
if (e->ev_updates == NULL) { |
||||
ares_event_thread_destroy_int(e); |
||||
return ARES_ENOMEM; |
||||
} |
||||
|
||||
e->ev_handles = ares__htable_asvp_create(ares_event_destroy_cb); |
||||
if (e->ev_handles == NULL) { |
||||
ares_event_thread_destroy_int(e); |
||||
return ARES_ENOMEM; |
||||
} |
||||
|
||||
e->channel = channel; |
||||
e->isup = ARES_TRUE; |
||||
e->ev_sys = ares_event_fetch_sys(channel->evsys); |
||||
if (e->ev_sys == NULL) { |
||||
ares_event_thread_destroy_int(e); |
||||
return ARES_ENOTIMP; |
||||
} |
||||
|
||||
channel->sock_state_cb = ares_event_thread_sockstate_cb; |
||||
channel->sock_state_cb_data = e; |
||||
|
||||
if (!e->ev_sys->init(e)) { |
||||
ares_event_thread_destroy_int(e); |
||||
channel->sock_state_cb = NULL; |
||||
channel->sock_state_cb_data = NULL; |
||||
return ARES_ESERVFAIL; |
||||
} |
||||
|
||||
/* Before starting the thread, process any possible events the initialization
|
||||
* might have enqueued as we may actually depend on these being valid |
||||
* immediately upon return, which may mean before the thread is fully spawned |
||||
* and processed the list itself. We don't want any sort of race conditions |
||||
* (like the event system wake handle itself). */ |
||||
ares_event_process_updates(e); |
||||
|
||||
/* Start thread */ |
||||
if (ares__thread_create(&e->thread, ares_event_thread, e) != ARES_SUCCESS) { |
||||
ares_event_thread_destroy_int(e); |
||||
channel->sock_state_cb = NULL; |
||||
channel->sock_state_cb_data = NULL; |
||||
return ARES_ESERVFAIL; |
||||
} |
||||
|
||||
return ARES_SUCCESS; |
||||
} |
@ -0,0 +1,166 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#include "ares_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
#include "ares_event.h" |
||||
#ifdef HAVE_UNISTD_H |
||||
# include <unistd.h> |
||||
#endif |
||||
#ifdef HAVE_FCNTL_H |
||||
# include <fcntl.h> |
||||
#endif |
||||
|
||||
#ifdef HAVE_PIPE |
||||
typedef struct { |
||||
int filedes[2]; |
||||
} ares_pipeevent_t; |
||||
|
||||
static void ares_pipeevent_destroy(ares_pipeevent_t *p) |
||||
{ |
||||
if (p->filedes[0] != -1) { |
||||
close(p->filedes[0]); |
||||
} |
||||
if (p->filedes[1] != -1) { |
||||
close(p->filedes[1]); |
||||
} |
||||
|
||||
ares_free(p); |
||||
} |
||||
|
||||
static void ares_pipeevent_destroy_cb(void *arg) |
||||
{ |
||||
ares_pipeevent_destroy(arg); |
||||
} |
||||
|
||||
static ares_pipeevent_t *ares_pipeevent_init(void) |
||||
{ |
||||
ares_pipeevent_t *p = ares_malloc_zero(sizeof(*p)); |
||||
if (p == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
p->filedes[0] = -1; |
||||
p->filedes[1] = -1; |
||||
|
||||
# ifdef HAVE_PIPE2 |
||||
if (pipe2(p->filedes, O_NONBLOCK | O_CLOEXEC) != 0) { |
||||
ares_pipeevent_destroy(p); |
||||
return NULL; |
||||
} |
||||
# else |
||||
if (pipe(p->filedes) != 0) { |
||||
ares_pipeevent_destroy(p); |
||||
return NULL; |
||||
} |
||||
|
||||
# ifdef O_NONBLOCK |
||||
{ |
||||
int val; |
||||
val = fcntl(p->filedes[0], F_GETFL, 0); |
||||
if (val >= 0) { |
||||
val |= O_NONBLOCK; |
||||
} |
||||
fcntl(p->filedes[0], F_SETFL, val); |
||||
|
||||
val = fcntl(p->filedes[1], F_GETFL, 0); |
||||
if (val >= 0) { |
||||
val |= O_NONBLOCK; |
||||
} |
||||
fcntl(p->filedes[1], F_SETFL, val); |
||||
} |
||||
# endif |
||||
|
||||
# ifdef O_CLOEXEC |
||||
fcntl(p->filedes[0], F_SETFD, O_CLOEXEC); |
||||
fcntl(p->filedes[1], F_SETFD, O_CLOEXEC); |
||||
# endif |
||||
# endif |
||||
|
||||
# ifdef F_SETNOSIGPIPE |
||||
fcntl(p->filedes[0], F_SETNOSIGPIPE, 1); |
||||
fcntl(p->filedes[1], F_SETNOSIGPIPE, 1); |
||||
# endif |
||||
|
||||
return p; |
||||
} |
||||
|
||||
static void ares_pipeevent_signal(const ares_event_t *e) |
||||
{ |
||||
ares_pipeevent_t *p; |
||||
|
||||
if (e == NULL || e->data == NULL) { |
||||
return; |
||||
} |
||||
|
||||
p = e->data; |
||||
write(p->filedes[1], "1", 1); |
||||
} |
||||
|
||||
static void ares_pipeevent_cb(ares_event_thread_t *e, ares_socket_t fd, |
||||
void *data, ares_event_flags_t flags) |
||||
{ |
||||
unsigned char buf[32]; |
||||
ares_pipeevent_t *p = NULL; |
||||
|
||||
(void)e; |
||||
(void)fd; |
||||
(void)flags; |
||||
|
||||
if (data == NULL) { |
||||
return; |
||||
} |
||||
|
||||
p = data; |
||||
|
||||
while (read(p->filedes[0], buf, sizeof(buf)) == sizeof(buf)) { |
||||
/* Do nothing */ |
||||
} |
||||
} |
||||
|
||||
ares_event_t *ares_pipeevent_create(ares_event_thread_t *e) |
||||
{ |
||||
ares_event_t *event = NULL; |
||||
ares_pipeevent_t *p = NULL; |
||||
ares_status_t status; |
||||
|
||||
p = ares_pipeevent_init(); |
||||
if (p == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
status = ares_event_update(&event, e, ARES_EVENT_FLAG_READ, ares_pipeevent_cb, |
||||
p->filedes[0], p, ares_pipeevent_destroy_cb, |
||||
ares_pipeevent_signal); |
||||
if (status != ARES_SUCCESS) { |
||||
ares_pipeevent_destroy(p); |
||||
return NULL; |
||||
} |
||||
|
||||
return event; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,601 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include "ares_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
#include "ares_event.h" |
||||
#include "ares_event_win32.h" |
||||
#ifdef HAVE_LIMITS_H |
||||
# include <limits.h> |
||||
#endif |
||||
|
||||
#ifdef _WIN32 |
||||
|
||||
/* IMPLEMENTATION NOTES
|
||||
* ==================== |
||||
* |
||||
* This implementation uses some undocumented functionality within Windows for |
||||
* monitoring sockets. The Ancillary Function Driver (AFD) is the low level |
||||
* implementation that Winsock2 sits on top of. Winsock2 unfortunately does |
||||
* not expose the equivalent of epoll() or kqueue(), but it is possible to |
||||
* access AFD directly and use along with IOCP to simulate the functionality. |
||||
* We want to use IOCP if possible as it gives us the ability to monitor more |
||||
* than just sockets (WSAPoll is not an option), and perform arbitrary callbacks |
||||
* which means we can hook in non-socket related events. |
||||
* |
||||
* The information for this implementation was gathered from "wepoll" and |
||||
* "libuv" which both use slight variants on this, but this implementation |
||||
* doesn't directly follow either methodology. |
||||
* |
||||
* Initialization: |
||||
* 1. Dynamically load the NtDeviceIoControlFile and NtCancelIoFileEx internal |
||||
* symbols from ntdll.dll. These functions are used to submit the AFD POLL |
||||
* request and to cancel a prior request, respectively. |
||||
* 2. Create an IO Completion Port base handle via CreateIoCompletionPort() |
||||
* that all socket events will be delivered through. |
||||
* 3. Create a callback to be used to be able to interrupt waiting for IOCP |
||||
* events, this may be called for allowing enqueuing of additional socket |
||||
* events or removing socket events. PostQueuedCompletionStatus() is the |
||||
* obvious choice. Use the same container structure as used with a Socket |
||||
* but tagged indicating it is not as the CompletionKey (important!). |
||||
* |
||||
* Socket Add: |
||||
* 1. Create/Allocate a container for holding metadata about a socket: |
||||
* - SOCKET base_socket; |
||||
* - SOCKET peer_socket; |
||||
* - OVERLAPPED overlapped; -- Used by AFD POLL |
||||
* - AFD_POLL_INFO afd_poll_info; -- Used by AFD POLL |
||||
* 2. Call WSAIoctl(..., SIO_BASE_HANDLE, ...) to unwrap the SOCKET and get |
||||
* the "base socket" we can use for polling. It appears this may fail so |
||||
* we should call WSAIoctl(..., SIO_BSP_HANDLE_POLL, ...) as a fallback. |
||||
* 3. The SOCKET handle we have is most likely not capable of supporting |
||||
* OVERLAPPED, and we need to have a way to unbind a socket from IOCP |
||||
* (which is done via a simple closesocket()) so we need to duplicate the |
||||
* "base socket" using WSADuplicateSocketW() followed by |
||||
* WSASocketW(..., WSA_FLAG_OVERLAPPED) to create this "peer socket" for |
||||
* submitting AFD POLL requests. |
||||
* 4. Bind to IOCP using CreateIoCompletionPort() referencing the "peer |
||||
* socket" and the base IOCP handle from "Initialization". Use the |
||||
* pointer to the socket container as the "CompletionKey" which will be |
||||
* returned when an event occurs. |
||||
* 5. Submit AFD POLL request (see "AFD POLL Request" section) |
||||
* |
||||
* Socket Delete: |
||||
* 1. Call "AFD Poll Cancel" (see Section of same name) |
||||
* 2. If a cancel was requested (not bypassed due to no events, etc), tag the |
||||
* "container" for the socket as pending delete, and when the next IOCP |
||||
* event for the socket is dequeued, cleanup. |
||||
* 3. Otherwise, call closesocket(peer_socket) then free() the container |
||||
* which will officially delete it. |
||||
* NOTE: Deferring delete may be completely unnecessary. In theory closing |
||||
* the peer_socket() should guarantee no additional events will be |
||||
* delivered. But maybe if there's a pending event that hasn't been |
||||
* read yet but already trigggered it would be an issue, so this is |
||||
* "safer" unless we can prove its not necessary. |
||||
* |
||||
* Socket Modify: |
||||
* 1. Call "AFD Poll Cancel" (see Section of same name) |
||||
* 2. If a cancel was not enqueued because there is no pending request, |
||||
* submit AFD POLL request (see "AFD POLL Request" section), otherwise |
||||
* defer until next socket event. |
||||
* |
||||
* Event Wait: |
||||
* 1. Call GetQueuedCompletionStatusEx() with the base IOCP handle, a |
||||
* stack allocated array of OVERLAPPED_ENTRY's, and an appropriate |
||||
* timeout. |
||||
* 2. Iterate across returned events, the CompletionKey is a pointer to the |
||||
* container registered with CreateIoCompletionPort() or |
||||
* PostQueuedCompletionStatus() |
||||
* 3. If object indicates it is pending delete, go ahead and |
||||
* closesocket(peer_socket) and free() the container. Go to the next event. |
||||
* 4. Submit AFD POLL Request (see "AFD POLL Request"). We must re-enable |
||||
* the request each time we receive a response, it is not persistent. |
||||
* 5. Notify of any events received as indicated in the AFD_POLL_INFO |
||||
* Handles[0].Events (NOTE: check NumberOfHandles first, make sure it is |
||||
* > 0, otherwise we might not have events such as if our last request |
||||
* was cancelled). |
||||
* |
||||
* AFD Poll Request: |
||||
* 1. Initialize the AFD_POLL_INFO structure: |
||||
* Exclusive = TRUE; // Auto cancel duplicates for same socket
|
||||
* NumberOfHandles = 1; |
||||
* Timeout.QuadPart = LLONG_MAX; |
||||
* Handles[0].Handle = (HANDLE)base_socket; |
||||
* Handles[0].Status = 0; |
||||
* Handles[0].Events = ... set as appropriate AFD_POLL_RECEIVE, etc; |
||||
* 2. Zero out the OVERLAPPED structure |
||||
* 3. Create an IO_STATUS_BLOCK pointer (iosb) and set it to the address of |
||||
* the OVERLAPPED "Internal" member. |
||||
* 4. Set the "Status" member of IO_STATUS_BLOCK to STATUS_PENDING |
||||
* 5. Call |
||||
* NtDeviceIoControlFile((HANDLE)peer_socket, NULL, NULL, &overlapped, |
||||
* iosb, IOCTL_AFD_POLL |
||||
* &afd_poll_info, sizeof(afd_poll_info), |
||||
* &afd_poll_info, sizeof(afd_poll_info)); |
||||
* NOTE: Its not clear to me if the IO_STATUS_BLOCK pointing to OVERLAPPED |
||||
* is for efficiency or if its a requirement for AFD. This is what |
||||
* libuv does, so I'm doing it here too. |
||||
* |
||||
* AFD Poll Cancel: |
||||
* 1. Check to see if the IO_STATUS_BLOCK "Status" member for the socket |
||||
* is still STATUS_PENDING, if not, no cancel request is necessary. |
||||
* 2. Call |
||||
* NtCancelIoFileEx((HANDLE)peer_socket, iosb, &temp_iosb); |
||||
* |
||||
* |
||||
* References: |
||||
* - https://github.com/piscisaureus/wepoll/
|
||||
* - https://github.com/libuv/libuv/
|
||||
*/ |
||||
|
||||
typedef struct { |
||||
/* Dynamically loaded symbols */ |
||||
NtDeviceIoControlFile_t NtDeviceIoControlFile; |
||||
NtCancelIoFileEx_t NtCancelIoFileEx; |
||||
|
||||
/* Implementation details */ |
||||
HANDLE iocp_handle; |
||||
} ares_evsys_win32_t; |
||||
|
||||
typedef struct { |
||||
/*! Pointer to parent event container */ |
||||
ares_event_t *event; |
||||
/*! Socket passed in to monitor */ |
||||
SOCKET socket; |
||||
/*! Base socket derived from provided socket */ |
||||
SOCKET base_socket; |
||||
/*! New socket (duplicate base_socket handle) supporting OVERLAPPED operation
|
||||
*/ |
||||
SOCKET peer_socket; |
||||
/*! Structure for submitting AFD POLL requests (Internals!) */ |
||||
AFD_POLL_INFO afd_poll_info; |
||||
/*! Overlapped structure submitted with AFD POLL requests and returned with
|
||||
* IOCP results */ |
||||
OVERLAPPED overlapped; |
||||
} ares_evsys_win32_eventdata_t; |
||||
|
||||
static void ares_iocpevent_signal(const ares_event_t *event) |
||||
{ |
||||
ares_event_thread_t *e = event->e; |
||||
ares_evsys_win32_t *ew = e->ev_sys_data; |
||||
|
||||
if (e == NULL) { |
||||
return; |
||||
} |
||||
|
||||
PostQueuedCompletionStatus(ew->iocp_handle, 0, (ULONG_PTR)event->data, NULL); |
||||
} |
||||
|
||||
static void ares_iocpevent_cb(ares_event_thread_t *e, ares_socket_t fd, |
||||
void *data, ares_event_flags_t flags) |
||||
{ |
||||
(void)e; |
||||
(void)data; |
||||
(void)fd; |
||||
(void)flags; |
||||
} |
||||
|
||||
static ares_event_t *ares_iocpevent_create(ares_event_thread_t *e) |
||||
{ |
||||
ares_event_t *event = NULL; |
||||
ares_status_t status; |
||||
|
||||
status = |
||||
ares_event_update(&event, e, ARES_EVENT_FLAG_OTHER, ares_iocpevent_cb, |
||||
ARES_SOCKET_BAD, NULL, NULL, ares_iocpevent_signal); |
||||
if (status != ARES_SUCCESS) { |
||||
return NULL; |
||||
} |
||||
|
||||
return event; |
||||
} |
||||
|
||||
static void ares_evsys_win32_destroy(ares_event_thread_t *e) |
||||
{ |
||||
ares_evsys_win32_t *ew = NULL; |
||||
|
||||
if (e == NULL) { |
||||
return; |
||||
} |
||||
|
||||
ew = e->ev_sys_data; |
||||
if (ew == NULL) { |
||||
return; |
||||
} |
||||
|
||||
if (ew->iocp_handle != NULL) { |
||||
CloseHandle(ew->iocp_handle); |
||||
} |
||||
|
||||
ares_free(ew); |
||||
e->ev_sys_data = NULL; |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_win32_init(ares_event_thread_t *e) |
||||
{ |
||||
ares_evsys_win32_t *ew = NULL; |
||||
HMODULE ntdll; |
||||
|
||||
ew = ares_malloc_zero(sizeof(*ew)); |
||||
if (ew == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
e->ev_sys_data = ew; |
||||
|
||||
/* All apps should have ntdll.dll already loaded, so just get a handle to
|
||||
* this */ |
||||
ntdll = GetModuleHandleA("ntdll.dll"); |
||||
if (ntdll == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
/* Load Internal symbols not typically accessible */ |
||||
ew->NtDeviceIoControlFile = (NtDeviceIoControlFile_t)(void *)GetProcAddress( |
||||
ntdll, "NtDeviceIoControlFile"); |
||||
ew->NtCancelIoFileEx = |
||||
(NtCancelIoFileEx_t)(void *)GetProcAddress(ntdll, "NtCancelIoFileEx"); |
||||
|
||||
if (ew->NtCancelIoFileEx == NULL || ew->NtDeviceIoControlFile == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
ew->iocp_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); |
||||
if (ew->iocp_handle == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
e->ev_signal = ares_iocpevent_create(e); |
||||
if (e->ev_signal == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
return ARES_TRUE; |
||||
|
||||
fail: |
||||
ares_evsys_win32_destroy(e); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
static ares_socket_t ares_evsys_win32_basesocket(ares_socket_t socket) |
||||
{ |
||||
while (1) { |
||||
DWORD bytes; /* Not used */ |
||||
ares_socket_t base_socket = ARES_SOCKET_BAD; |
||||
int rv; |
||||
|
||||
rv = WSAIoctl(socket, SIO_BASE_HANDLE, NULL, 0, &base_socket, |
||||
sizeof(base_socket), &bytes, NULL, NULL); |
||||
if (rv != SOCKET_ERROR && base_socket != ARES_SOCKET_BAD) { |
||||
socket = base_socket; |
||||
break; |
||||
} |
||||
|
||||
/* If we're here, an error occurred */ |
||||
if (GetLastError() == WSAENOTSOCK) { |
||||
/* This is critical, exit */ |
||||
return ARES_SOCKET_BAD; |
||||
} |
||||
|
||||
/* Work around known bug in Komodia based LSPs, use ARES_BSP_HANDLE_POLL
|
||||
* to retrieve the underlying socket to then loop and get the base socket: |
||||
* https://docs.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls
|
||||
* https://www.komodia.com/newwiki/index.php?title=Komodia%27s_Redirector_bug_fixes#Version_2.2.2.6
|
||||
*/ |
||||
base_socket = ARES_SOCKET_BAD; |
||||
rv = WSAIoctl(socket, SIO_BSP_HANDLE_POLL, NULL, 0, &base_socket, |
||||
sizeof(base_socket), &bytes, NULL, NULL); |
||||
|
||||
if (rv != SOCKET_ERROR && base_socket != ARES_SOCKET_BAD && |
||||
base_socket != socket) { |
||||
socket = base_socket; |
||||
continue; /* loop! */ |
||||
} |
||||
|
||||
return ARES_SOCKET_BAD; |
||||
} |
||||
|
||||
return socket; |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_win32_afd_enqueue(ares_event_t *event, |
||||
ares_event_flags_t flags) |
||||
{ |
||||
ares_event_thread_t *e = event->e; |
||||
ares_evsys_win32_t *ew = e->ev_sys_data; |
||||
ares_evsys_win32_eventdata_t *ed = event->data; |
||||
NTSTATUS status; |
||||
IO_STATUS_BLOCK *iosb_ptr; |
||||
|
||||
if (e == NULL || ed == NULL || ew == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
/* Enqueue AFD Poll */ |
||||
ed->afd_poll_info.Exclusive = TRUE; |
||||
ed->afd_poll_info.NumberOfHandles = 1; |
||||
ed->afd_poll_info.Timeout.QuadPart = LLONG_MAX; |
||||
ed->afd_poll_info.Handles[0].Handle = (HANDLE)ed->base_socket; |
||||
ed->afd_poll_info.Handles[0].Status = 0; |
||||
ed->afd_poll_info.Handles[0].Events = 0; |
||||
|
||||
if (flags & ARES_EVENT_FLAG_READ) { |
||||
ed->afd_poll_info.Handles[0].Events |= |
||||
(AFD_POLL_RECEIVE | AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | |
||||
AFD_POLL_ABORT); |
||||
} |
||||
if (flags & ARES_EVENT_FLAG_WRITE) { |
||||
ed->afd_poll_info.Handles[0].Events |= |
||||
(AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL); |
||||
} |
||||
if (flags == 0) { |
||||
ed->afd_poll_info.Handles[0].Events |= AFD_POLL_DISCONNECT; |
||||
} |
||||
|
||||
memset(&ed->overlapped, 0, sizeof(ed->overlapped)); |
||||
iosb_ptr = (IO_STATUS_BLOCK *)&ed->overlapped.Internal; |
||||
iosb_ptr->Status = STATUS_PENDING; |
||||
|
||||
status = ew->NtDeviceIoControlFile( |
||||
(HANDLE)ed->peer_socket, NULL, NULL, &ed->overlapped, iosb_ptr, |
||||
IOCTL_AFD_POLL, &ed->afd_poll_info, sizeof(ed->afd_poll_info), |
||||
&ed->afd_poll_info, sizeof(ed->afd_poll_info)); |
||||
if (status != STATUS_SUCCESS && status != STATUS_PENDING) { |
||||
printf("%s(): failed to perform IOCTL_AFD_POLL operation\n", __FUNCTION__); |
||||
fflush(stdout); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_win32_afd_cancel(ares_evsys_win32_eventdata_t *ed) |
||||
{ |
||||
IO_STATUS_BLOCK *iosb_ptr; |
||||
IO_STATUS_BLOCK cancel_iosb; |
||||
ares_evsys_win32_t *ew; |
||||
NTSTATUS status; |
||||
|
||||
/* Detached due to destroy */ |
||||
if (ed->event == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
iosb_ptr = (IO_STATUS_BLOCK *)&ed->overlapped.Internal; |
||||
/* Not pending, nothing to do */ |
||||
if (iosb_ptr->Status != STATUS_PENDING) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
ew = ed->event->e->ev_sys_data; |
||||
status = |
||||
ew->NtCancelIoFileEx((HANDLE)ed->peer_socket, iosb_ptr, &cancel_iosb); |
||||
|
||||
/* NtCancelIoFileEx() may return STATUS_NOT_FOUND if the operation completed
|
||||
* just before calling NtCancelIoFileEx(), but we have not yet received the |
||||
* notifiction (but it should be queued for the next IOCP event). */ |
||||
if (status == STATUS_SUCCESS || status == STATUS_NOT_FOUND) { |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
static void ares_evsys_win32_eventdata_destroy(ares_evsys_win32_eventdata_t *ed) |
||||
{ |
||||
if (ed == NULL) { |
||||
return; |
||||
} |
||||
|
||||
if (ed->peer_socket != ARES_SOCKET_BAD) { |
||||
closesocket(ed->peer_socket); |
||||
} |
||||
|
||||
ares_free(ed); |
||||
} |
||||
|
||||
static ares_bool_t ares_evsys_win32_event_add(ares_event_t *event) |
||||
{ |
||||
ares_event_thread_t *e = event->e; |
||||
ares_evsys_win32_t *ew = e->ev_sys_data; |
||||
ares_evsys_win32_eventdata_t *ed; |
||||
WSAPROTOCOL_INFOW protocol_info; |
||||
|
||||
ed = ares_malloc_zero(sizeof(*ed)); |
||||
ed->event = event; |
||||
ed->socket = event->fd; |
||||
ed->base_socket = ARES_SOCKET_BAD; |
||||
ed->peer_socket = ARES_SOCKET_BAD; |
||||
|
||||
/* Likely a signal event, not something we will directly handle. We create
|
||||
* the ares_evsys_win32_eventdata_t as the placeholder to use as the |
||||
* IOCP Completion Key */ |
||||
if (ed->socket == ARES_SOCKET_BAD) { |
||||
event->data = ed; |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
ed->base_socket = ares_evsys_win32_basesocket(ed->socket); |
||||
if (ed->base_socket == ARES_SOCKET_BAD) { |
||||
fprintf(stderr, "%s(): could not determine base socket for fd %d\n", |
||||
__FUNCTION__, (int)event->fd); |
||||
ares_evsys_win32_eventdata_destroy(ed); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
/* Create a peer socket that supports OVERLAPPED so we can use IOCP on the
|
||||
* socket handle */ |
||||
if (WSADuplicateSocketW(ed->base_socket, GetCurrentProcessId(), |
||||
&protocol_info) != 0) { |
||||
fprintf(stderr, |
||||
"%s(): could not retrieve protocol info for creating peer socket\n", |
||||
__FUNCTION__); |
||||
ares_evsys_win32_eventdata_destroy(ed); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
ed->peer_socket = |
||||
WSASocketW(protocol_info.iAddressFamily, protocol_info.iSocketType, |
||||
protocol_info.iProtocol, &protocol_info, 0, WSA_FLAG_OVERLAPPED); |
||||
if (ed->peer_socket == ARES_SOCKET_BAD) { |
||||
fprintf(stderr, "%s(): could not create peer socket\n", __FUNCTION__); |
||||
ares_evsys_win32_eventdata_destroy(ed); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
SetHandleInformation((HANDLE)ed->peer_socket, HANDLE_FLAG_INHERIT, 0); |
||||
|
||||
if (CreateIoCompletionPort((HANDLE)ed->peer_socket, ew->iocp_handle, |
||||
(ULONG_PTR)ed, 0) == NULL) { |
||||
fprintf(stderr, "%s(): failed to bind peer socket to IOCP\n", __FUNCTION__); |
||||
ares_evsys_win32_eventdata_destroy(ed); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
event->data = ed; |
||||
|
||||
if (!ares_evsys_win32_afd_enqueue(event, event->flags)) { |
||||
event->data = NULL; |
||||
ares_evsys_win32_eventdata_destroy(ed); |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
static void ares_evsys_win32_event_del(ares_event_t *event) |
||||
{ |
||||
ares_evsys_win32_eventdata_t *ed = event->data; |
||||
ares_event_thread_t *e = event->e; |
||||
|
||||
if (event->fd == ARES_SOCKET_BAD || !e->isup || ed == NULL || |
||||
!ares_evsys_win32_afd_cancel(ed)) { |
||||
/* Didn't need to enqueue a cancellation, for one of these reasons:
|
||||
* - Not an IOCP socket |
||||
* - This is during shutdown of the event thread, no more signals can be |
||||
* delivered. |
||||
* - It has been determined there is no AFD POLL queued currently for the |
||||
* socket. |
||||
*/ |
||||
ares_evsys_win32_eventdata_destroy(ed); |
||||
event->data = NULL; |
||||
} else { |
||||
/* Detach from event, so when the cancel event comes through,
|
||||
* it will clean up */ |
||||
ed->event = NULL; |
||||
event->data = NULL; |
||||
} |
||||
} |
||||
|
||||
static void ares_evsys_win32_event_mod(ares_event_t *event, |
||||
ares_event_flags_t new_flags) |
||||
{ |
||||
ares_evsys_win32_eventdata_t *ed = event->data; |
||||
|
||||
/* Not for us */ |
||||
if (event->fd == ARES_SOCKET_BAD || ed == NULL) { |
||||
return; |
||||
} |
||||
|
||||
/* Try to cancel any current outstanding poll, if one is not running,
|
||||
* go ahead and queue it up */ |
||||
if (!ares_evsys_win32_afd_cancel(ed)) { |
||||
ares_evsys_win32_afd_enqueue(event, new_flags); |
||||
} |
||||
} |
||||
|
||||
static size_t ares_evsys_win32_wait(ares_event_thread_t *e, |
||||
unsigned long timeout_ms) |
||||
{ |
||||
ares_evsys_win32_t *ew = e->ev_sys_data; |
||||
OVERLAPPED_ENTRY entries[16]; |
||||
ULONG nentries = sizeof(entries) / sizeof(*entries); |
||||
BOOL status; |
||||
size_t i; |
||||
size_t cnt = 0; |
||||
|
||||
status = GetQueuedCompletionStatusEx( |
||||
ew->iocp_handle, entries, nentries, &nentries, |
||||
(timeout_ms == 0) ? INFINITE : (DWORD)timeout_ms, FALSE); |
||||
|
||||
if (!status) { |
||||
return 0; |
||||
} |
||||
|
||||
for (i = 0; i < (size_t)nentries; i++) { |
||||
ares_event_flags_t flags = 0; |
||||
ares_evsys_win32_eventdata_t *ed = |
||||
(ares_evsys_win32_eventdata_t *)entries[i].lpCompletionKey; |
||||
ares_event_t *event = ed->event; |
||||
|
||||
if (ed->socket == ARES_SOCKET_BAD) { |
||||
/* Some sort of signal event */ |
||||
flags = ARES_EVENT_FLAG_OTHER; |
||||
} else { |
||||
/* Process events */ |
||||
if (ed->afd_poll_info.NumberOfHandles > 0) { |
||||
if (ed->afd_poll_info.Handles[0].Events & |
||||
(AFD_POLL_RECEIVE | AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | |
||||
AFD_POLL_ABORT)) { |
||||
flags |= ARES_EVENT_FLAG_READ; |
||||
} |
||||
if (ed->afd_poll_info.Handles[0].Events & |
||||
(AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL)) { |
||||
flags |= ARES_EVENT_FLAG_WRITE; |
||||
} |
||||
|
||||
/* XXX: Handle ed->afd_poll_info.Handles[0].Events &
|
||||
* AFD_POLL_LOCAL_CLOSE */ |
||||
} |
||||
|
||||
if (event == NULL) { |
||||
/* This means we need to cleanup the private event data as we've been
|
||||
* detached */ |
||||
ares_evsys_win32_eventdata_destroy(ed); |
||||
} else { |
||||
/* Re-enqueue so we can get more events on the socket */ |
||||
ares_evsys_win32_afd_enqueue(event, event->flags); |
||||
} |
||||
} |
||||
|
||||
if (event != NULL && flags != 0) { |
||||
cnt++; |
||||
event->cb(e, event->fd, event->data, flags); |
||||
} |
||||
} |
||||
|
||||
return cnt; |
||||
} |
||||
|
||||
const ares_event_sys_t ares_evsys_win32 = { "win32", |
||||
ares_evsys_win32_init, |
||||
ares_evsys_win32_destroy, |
||||
ares_evsys_win32_event_add, |
||||
ares_evsys_win32_event_del, |
||||
ares_evsys_win32_event_mod, |
||||
ares_evsys_win32_wait }; |
||||
#endif |
@ -0,0 +1,119 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#ifndef __ARES_EVENT_WIN32_H |
||||
#define __ARES_EVENT_WIN32_H |
||||
|
||||
#ifdef _WIN32 |
||||
# ifdef HAVE_WINSOCK2_H |
||||
# include <winsock2.h> |
||||
# endif |
||||
# ifdef HAVE_WS2TCPIP_H |
||||
# include <ws2tcpip.h> |
||||
# endif |
||||
# ifdef HAVE_MSWSOCK_H |
||||
# include <mswsock.h> |
||||
# endif |
||||
# ifdef HAVE_WINDOWS_H |
||||
# include <windows.h> |
||||
# endif |
||||
|
||||
/* From winternl.h */ |
||||
|
||||
/* If WDK is not installed and not using MinGW, provide the needed definitions
|
||||
*/ |
||||
typedef LONG NTSTATUS; |
||||
|
||||
typedef struct _IO_STATUS_BLOCK { |
||||
union { |
||||
NTSTATUS Status; |
||||
PVOID Pointer; |
||||
}; |
||||
|
||||
ULONG_PTR Information; |
||||
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; |
||||
|
||||
typedef VOID(NTAPI *PIO_APC_ROUTINE)(PVOID ApcContext, |
||||
PIO_STATUS_BLOCK IoStatusBlock, |
||||
ULONG Reserved); |
||||
|
||||
/* From ntstatus.h */ |
||||
# define STATUS_SUCCESS ((NTSTATUS)0x00000000) |
||||
# ifndef STATUS_PENDING |
||||
# define STATUS_PENDING ((NTSTATUS)0x00000103L) |
||||
# endif |
||||
# define STATUS_CANCELLED ((NTSTATUS)0xC0000120L) |
||||
# define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) |
||||
|
||||
/* Not sure what headers might have these */ |
||||
# define IOCTL_AFD_POLL 0x00012024 |
||||
|
||||
# define AFD_POLL_RECEIVE 0x0001 |
||||
# define AFD_POLL_RECEIVE_EXPEDITED 0x0002 |
||||
# define AFD_POLL_SEND 0x0004 |
||||
# define AFD_POLL_DISCONNECT 0x0008 |
||||
# define AFD_POLL_ABORT 0x0010 |
||||
# define AFD_POLL_LOCAL_CLOSE 0x0020 |
||||
# define AFD_POLL_ACCEPT 0x0080 |
||||
# define AFD_POLL_CONNECT_FAIL 0x0100 |
||||
|
||||
typedef struct _AFD_POLL_HANDLE_INFO { |
||||
HANDLE Handle; |
||||
ULONG Events; |
||||
NTSTATUS Status; |
||||
} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO; |
||||
|
||||
typedef struct _AFD_POLL_INFO { |
||||
LARGE_INTEGER Timeout; |
||||
ULONG NumberOfHandles; |
||||
ULONG Exclusive; |
||||
AFD_POLL_HANDLE_INFO Handles[1]; |
||||
} AFD_POLL_INFO, *PAFD_POLL_INFO; |
||||
|
||||
/* Prototypes for dynamically loaded functions from ntdll.dll */ |
||||
typedef NTSTATUS(NTAPI *NtCancelIoFileEx_t)(HANDLE FileHandle, |
||||
PIO_STATUS_BLOCK IoRequestToCancel, |
||||
PIO_STATUS_BLOCK IoStatusBlock); |
||||
typedef NTSTATUS(NTAPI *NtDeviceIoControlFile_t)( |
||||
HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, |
||||
PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, |
||||
ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength); |
||||
|
||||
/* On UWP/Windows Store, these definitions aren't there for some reason */ |
||||
# ifndef SIO_BSP_HANDLE_POLL |
||||
# define SIO_BSP_HANDLE_POLL 0x4800001D |
||||
# endif |
||||
|
||||
# ifndef SIO_BASE_HANDLE |
||||
# define SIO_BASE_HANDLE 0x48000022 |
||||
# endif |
||||
|
||||
# ifndef HANDLE_FLAG_INHERIT |
||||
# define HANDLE_FLAG_INHERIT 0x00000001 |
||||
# endif |
||||
|
||||
#endif /* _WIN32 */ |
||||
|
||||
#endif /* __ARES_EVENT_WIN32_H */ |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue