Ctrl K
ComunidadGuías…

toxcore/os_event.c

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/* SPDX-License-Identifier: GPL-3.0-or-later
 * Copyright © 2026 The TokTok team.
 */

#include "os_event.h"

#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#include <winsock2.h>
#include <winternl.h>
#else
#include <unistd.h>
#endif /* WIN32 */

#ifdef EV_USE_EPOLL
#include <sys/epoll.h>
#elif defined(ESP_PLATFORM)
#include <sys/poll.h>
#elif !defined(_WIN32)
#include <poll.h>
#endif /* EV_USE_EPOLL */

#include "ccompat.h"
#include "logger.h"
#include "mem.h"

#ifdef _WIN32
// Using the undocumented AFD driver directly (IOCTL_AFD_POLL).
// This bypasses the 64-handle limit of WaitForMultipleEvents and the bugs of WSAPoll.
// It provides true Level-Triggered notifications similar to poll/epoll.

#define IOCTL_AFD_POLL 0x00012024

typedef struct AFD_Poll_Handle_Info {
    void *_Nullable handle;
    unsigned long events;
    uint32_t status;
} AFD_Poll_Handle_Info;

typedef struct AFD_Poll_Info {
    int64_t timeout;
    unsigned long number_of_handles;
    unsigned long exclusive;
    AFD_Poll_Handle_Info handles[1];
} AFD_Poll_Info;

typedef enum Afd_Poll {
    AFD_POLL_RECEIVE = 0x0001,
    AFD_POLL_RECEIVE_EXPEDITED = 0x0002,
    AFD_POLL_SEND = 0x0004,
    AFD_POLL_DISCONNECT = 0x0008,
    AFD_POLL_ABORT = 0x0010,
    AFD_POLL_LOCAL_CLOSE = 0x0020,
    AFD_POLL_ACCEPT = 0x0080,
    AFD_POLL_CONNECT_FAIL = 0x0100,
} Afd_Poll;
#endif /* _WIN32 */

typedef struct OS_Ev_Registration {
    Socket sock;
    Ev_Events events;
    void *_Nullable data;
} OS_Ev_Registration;

typedef struct OS_Ev {
    const Memory *_Nonnull mem;
    const Logger *_Nonnull log;
    OS_Ev_Registration **_Nonnull regs;
    uint32_t regs_count;
    uint32_t regs_capacity;

#ifdef EV_USE_EPOLL
    int epoll_fd;
    struct epoll_event *events;
    uint32_t events_capacity;
#elif defined(_WIN32)
    AFD_Poll_Handle_Info *h_events;
    uint32_t h_events_capacity;
    SOCKET helper_sock;
#else
    struct pollfd *pfds;
    uint32_t pfds_count;
#endif /* EV_USE_EPOLL */
} OS_Ev;

static OS_Ev_Registration *_Nullable os_ev_prepare_add(OS_Ev *_Nonnull os_ev, Socket sock, Ev_Events events,
        void *_Nullable data)
{
    for (uint32_t i = 0; i < os_ev->regs_count; ++i) {
        if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) {
            return nullptr;
        }
    }

    if (os_ev->regs_count == os_ev->regs_capacity) {
        const uint32_t new_capacity = os_ev->regs_capacity == 0 ? 8 : os_ev->regs_capacity * 2;
        OS_Ev_Registration **new_regs
            = (OS_Ev_Registration **)mem_vrealloc(os_ev->mem, os_ev->regs, new_capacity, sizeof(OS_Ev_Registration *));

        if (new_regs == nullptr) {
            return nullptr;
        }

        os_ev->regs = new_regs;
        os_ev->regs_capacity = new_capacity;

#ifdef _WIN32
        AFD_Poll_Handle_Info *new_afd = (AFD_Poll_Handle_Info *)mem_vrealloc(
                                            os_ev->mem, os_ev->h_events, new_capacity, sizeof(AFD_Poll_Handle_Info));
        if (new_afd == nullptr) {
            return nullptr;
        }
        os_ev->h_events = new_afd;
        os_ev->h_events_capacity = new_capacity;
#endif /* _WIN32 */
    }

    OS_Ev_Registration *reg = (OS_Ev_Registration *)mem_alloc(os_ev->mem, sizeof(OS_Ev_Registration));
    if (reg == nullptr) {
        return nullptr;
    }
    reg->sock = sock;
    reg->events = events;
    reg->data = data;

    return reg;
}

/** @brief Adds a registration from os_ev_prepare_add once the backend has accepted it. */
static void os_ev_commit_add(OS_Ev *_Nonnull os_ev, OS_Ev_Registration *_Nonnull reg)
{
    os_ev->regs[os_ev->regs_count] = reg;
    ++os_ev->regs_count;
}

#ifdef EV_USE_EPOLL

static uint32_t events_to_epoll(Ev_Events events)
{
    uint32_t e = 0;

    if ((events & EV_READ) != 0) {
        e |= EPOLLIN;
    }

    if ((events & EV_WRITE) != 0) {
        e |= EPOLLOUT;
    }

    return e;
}

static Ev_Events epoll_to_events(uint32_t e)
{
    Ev_Events events = 0;

    if ((e & EPOLLIN) != 0) {
        events |= EV_READ;
    }

    if ((e & EPOLLOUT) != 0) {
        events |= EV_WRITE;
    }

    if ((e & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) != 0) {
        events |= EV_ERROR;
    }

    return events;
}

static bool os_ev_add(void *_Nonnull self, Socket sock, Ev_Events events, void *_Nullable data)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    OS_Ev_Registration *reg = os_ev_prepare_add(os_ev, sock, events, data);
    if (reg == nullptr) {
        return false;
    }

    struct epoll_event ep_ev;
    ep_ev.events = events_to_epoll(events);
    ep_ev.data.ptr = reg;

    if (epoll_ctl(os_ev->epoll_fd, EPOLL_CTL_ADD, net_socket_to_native(sock), &ep_ev) == -1) {
        Net_Strerror error_buf;
        LOGGER_ERROR(os_ev->log, "epoll_ctl(ADD) failed: %s", net_strerror(errno, &error_buf));
        mem_delete(os_ev->mem, reg);
        return false;
    }

    LOGGER_DEBUG(os_ev->log, "Added socket %d to epoll (events: %u)", net_socket_to_native(sock), events);

    os_ev_commit_add(os_ev, reg);
    return true;
}

static bool os_ev_mod(void *_Nonnull self, Socket sock, Ev_Events events, void *_Nullable data)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    for (uint32_t i = 0; i < os_ev->regs_count; ++i) {
        if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) {
            OS_Ev_Registration *reg = os_ev->regs[i];
            reg->events = events;
            reg->data = data;

            struct epoll_event ep_ev;
            ep_ev.events = events_to_epoll(events);
            ep_ev.data.ptr = reg;

            if (epoll_ctl(os_ev->epoll_fd, EPOLL_CTL_MOD, net_socket_to_native(sock), &ep_ev) == -1) {
                Net_Strerror error_buf;
                LOGGER_ERROR(os_ev->log, "epoll_ctl(MOD) failed: %s", net_strerror(errno, &error_buf));
                return false;
            }

            LOGGER_DEBUG(os_ev->log, "Modified socket %d in epoll (events: %u)", net_socket_to_native(sock), events);

            return true;
        }
    }

    return false;
}

static bool os_ev_del(void *_Nonnull self, Socket sock)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    for (uint32_t i = 0; i < os_ev->regs_count; ++i) {
        if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) {
            if (epoll_ctl(os_ev->epoll_fd, EPOLL_CTL_DEL, net_socket_to_native(sock), nullptr) == -1) {
                Net_Strerror error_buf;
                LOGGER_ERROR(os_ev->log, "epoll_ctl(DEL) failed: %s", net_strerror(errno, &error_buf));
            }

            LOGGER_DEBUG(os_ev->log, "Removed socket %d from epoll", net_socket_to_native(sock));

            mem_delete(os_ev->mem, os_ev->regs[i]);
            os_ev->regs[i] = os_ev->regs[os_ev->regs_count - 1];
            --os_ev->regs_count;
            return true;
        }
    }

    return false;
}

static int32_t os_ev_run(void *_Nonnull self, Ev_Result *_Nonnull results, uint32_t max_results, int32_t timeout_ms)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    if (os_ev->events_capacity < max_results) {
        struct epoll_event *new_events
            = (struct epoll_event *)mem_vrealloc(os_ev->mem, os_ev->events, max_results, sizeof(struct epoll_event));

        if (new_events == nullptr) {
            return -1;
        }

        os_ev->events = new_events;
        os_ev->events_capacity = max_results;
    }

    const int n = epoll_wait(os_ev->epoll_fd, os_ev->events, (int)max_results, (int)timeout_ms);

    if (n < 0) {
        if (errno == EINTR) {
            return 0;
        }

        Net_Strerror error_buf;
        LOGGER_ERROR(os_ev->log, "epoll_wait failed: %s", net_strerror(errno, &error_buf));
        return -1;
    }

    if (n == 0) {
        return 0;
    }

    uint32_t count = 0;

    for (int i = 0; i < n; ++i) {
        OS_Ev_Registration *reg = (OS_Ev_Registration *)os_ev->events[i].data.ptr;

        if (reg != nullptr) {
            results[count].sock = reg->sock;
            results[count].events = epoll_to_events(os_ev->events[i].events);
            results[count].data = reg->data;
            ++count;
        }
    }

    return (int32_t)count;
}

#elif defined(_WIN32)

static uint32_t events_to_afd(Ev_Events events)
{
    uint32_t e = AFD_POLL_ABORT | AFD_POLL_LOCAL_CLOSE | AFD_POLL_CONNECT_FAIL;  // Always monitor errors

    if (events & EV_READ) {
        e |= AFD_POLL_RECEIVE | AFD_POLL_RECEIVE_EXPEDITED | AFD_POLL_ACCEPT | AFD_POLL_DISCONNECT;
    }

    if (events & EV_WRITE) {
        e |= AFD_POLL_SEND;
    }

    return e;
}

static Ev_Events afd_to_events(uint32_t e)
{
    Ev_Events events = 0;

    if (e & (AFD_POLL_RECEIVE | AFD_POLL_RECEIVE_EXPEDITED | AFD_POLL_ACCEPT | AFD_POLL_DISCONNECT)) {
        events |= EV_READ;
    }

    if (e & AFD_POLL_SEND) {
        events |= EV_WRITE;
    }

    if (e & (AFD_POLL_ABORT | AFD_POLL_LOCAL_CLOSE | AFD_POLL_CONNECT_FAIL)) {
        events |= EV_ERROR;
    }

    return events;
}

static bool os_ev_add(void *_Nonnull self, Socket sock, Ev_Events events, void *_Nullable data)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    OS_Ev_Registration *reg = os_ev_prepare_add(os_ev, sock, events, data);
    if (reg == nullptr) {
        return false;
    }

    AFD_Poll_Handle_Info *afd_handles = os_ev->h_events;
    afd_handles[os_ev->regs_count].handle = (HANDLE)(uintptr_t)net_socket_to_native(sock);
    afd_handles[os_ev->regs_count].events = events_to_afd(events);
    afd_handles[os_ev->regs_count].status = 0;

    LOGGER_DEBUG(os_ev->log, "Added socket %d to AFD (events: %u)", net_socket_to_native(sock), events);

    os_ev_commit_add(os_ev, reg);

    return true;
}

static bool os_ev_mod(void *_Nonnull self, Socket sock, Ev_Events events, void *_Nullable data)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    for (uint32_t i = 0; i < os_ev->regs_count; ++i) {
        if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) {
            os_ev->regs[i]->events = events;
            os_ev->regs[i]->data = data;

            AFD_Poll_Handle_Info *afd_handles = os_ev->h_events;
            afd_handles[i].events = events_to_afd(events);
            afd_handles[i].status = 0;

            LOGGER_DEBUG(os_ev->log, "Modified socket %d in AFD (events: %u)", net_socket_to_native(sock), events);

            return true;
        }
    }

    return false;
}

static bool os_ev_del(void *_Nonnull self, Socket sock)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    for (uint32_t i = 0; i < os_ev->regs_count; ++i) {
        if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) {
            mem_delete(os_ev->mem, os_ev->regs[i]);
            os_ev->regs[i] = os_ev->regs[os_ev->regs_count - 1];

            AFD_Poll_Handle_Info *afd_handles = os_ev->h_events;
            afd_handles[i] = afd_handles[os_ev->regs_count - 1];

            --os_ev->regs_count;

            LOGGER_DEBUG(os_ev->log, "Removed socket %d from AFD", net_socket_to_native(sock));

            return true;
        }
    }

    return false;
}

static int32_t os_ev_run(void *_Nonnull self, Ev_Result *_Nonnull results, uint32_t max_results, int32_t timeout_ms)
{
    OS_Ev *os_ev = (OS_Ev *)self;
    if (os_ev->regs_count == 0) {
        if (timeout_ms > 0) {
            Sleep(timeout_ms);
        }
        return 0;
    }

    const size_t info_size = sizeof(AFD_Poll_Info) + (os_ev->regs_count - 1) * sizeof(AFD_Poll_Handle_Info);
    AFD_Poll_Info *info = (AFD_Poll_Info *)HeapAlloc(GetProcessHeap(), 0, info_size);
    if (info == nullptr) {
        return -1;
    }

    info->timeout = timeout_ms < 0 ? INT64_MAX : (int64_t)timeout_ms * -10000;
    info->number_of_handles = os_ev->regs_count;
    info->exclusive = 0;

    memcpy(info->handles, os_ev->h_events, os_ev->regs_count * sizeof(AFD_Poll_Handle_Info));

    DWORD bytes_ret = 0;

    if (!DeviceIoControl((HANDLE)os_ev->helper_sock, IOCTL_AFD_POLL, info, (DWORD)info_size, info, (DWORD)info_size,
                         &bytes_ret, NULL)) {
        const int err = net_error();
        HeapFree(GetProcessHeap(), 0, info);

        if (err == WAIT_TIMEOUT) {
            return 0;
        }

        Net_Strerror error_buf;
        LOGGER_ERROR(os_ev->log, "AFD_POLL failed: %s", net_strerror(err, &error_buf));
        return -1;
    }

    uint32_t count = 0;
    for (uint32_t i = 0; i < info->number_of_handles && count < max_results; ++i) {
        if (info->handles[i].events != 0) {
            results[count].sock = os_ev->regs[i]->sock;
            results[count].data = os_ev->regs[i]->data;
            results[count].events = afd_to_events(info->handles[i].events);
            ++count;
        }
    }

    HeapFree(GetProcessHeap(), 0, info);
    return (int32_t)count;
}

#else  // POLL

static bool os_ev_add(void *_Nonnull self, Socket sock, Ev_Events events, void *_Nullable data)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    OS_Ev_Registration *reg = os_ev_prepare_add(os_ev, sock, events, data);
    if (reg == nullptr) {
        return false;
    }

    LOGGER_DEBUG(os_ev->log, "Added socket %d to poll (events: %u)", net_socket_to_native(sock), events);

    os_ev_commit_add(os_ev, reg);

    return true;
}

static bool os_ev_mod(void *_Nonnull self, Socket sock, Ev_Events events, void *_Nullable data)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    for (uint32_t i = 0; i < os_ev->regs_count; ++i) {
        if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) {
            os_ev->regs[i]->events = events;
            os_ev->regs[i]->data = data;

            LOGGER_DEBUG(os_ev->log, "Modified socket %d in poll (events: %u)", net_socket_to_native(sock), events);

            return true;
        }
    }

    return false;
}

static bool os_ev_del(void *_Nonnull self, Socket sock)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    for (uint32_t i = 0; i < os_ev->regs_count; ++i) {
        if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) {
            mem_delete(os_ev->mem, os_ev->regs[i]);
            os_ev->regs[i] = os_ev->regs[os_ev->regs_count - 1];
            --os_ev->regs_count;

            LOGGER_DEBUG(os_ev->log, "Removed socket %d from poll", net_socket_to_native(sock));

            return true;
        }
    }

    return false;
}

static int32_t os_ev_run(void *_Nonnull self, Ev_Result *_Nonnull results, uint32_t max_results, int32_t timeout_ms)
{
    OS_Ev *os_ev = (OS_Ev *)self;

    if (os_ev->pfds_count < os_ev->regs_count) {
        struct pollfd *new_pfds
            = (struct pollfd *)mem_vrealloc(os_ev->mem, os_ev->pfds, os_ev->regs_count, sizeof(struct pollfd));

        if (new_pfds == nullptr) {
            return -1;
        }

        os_ev->pfds = new_pfds;
        os_ev->pfds_count = os_ev->regs_count;
    }

    for (uint32_t i = 0; i < os_ev->regs_count; ++i) {
        os_ev->pfds[i].fd = net_socket_to_native(os_ev->regs[i]->sock);
        os_ev->pfds[i].events = 0;

        if ((os_ev->regs[i]->events & EV_READ) != 0) {
            os_ev->pfds[i].events |= POLLIN;
        }

        if ((os_ev->regs[i]->events & EV_WRITE) != 0) {
            os_ev->pfds[i].events |= POLLOUT;
        }

        os_ev->pfds[i].revents = 0;
    }

    const int n = poll(os_ev->pfds, (nfds_t)os_ev->regs_count, (int)timeout_ms);

    if (n < 0) {
        if (errno == EINTR) {
            return 0;
        }

        Net_Strerror error_buf;
        LOGGER_ERROR(os_ev->log, "poll failed: %s", net_strerror(errno, &error_buf));
        return -1;
    }

    if (n == 0) {
        return 0;
    }

    uint32_t count = 0;

    for (uint32_t i = 0; i < os_ev->regs_count && count < max_results; ++i) {
        if (os_ev->pfds[i].revents == 0) {
            continue;
        }

        results[count].sock = os_ev->regs[i]->sock;
        results[count].events = 0;
        results[count].data = os_ev->regs[i]->data;

        if ((os_ev->pfds[i].revents & POLLIN) != 0) {
            results[count].events |= EV_READ;
        }

        if ((os_ev->pfds[i].revents & POLLOUT) != 0) {
            results[count].events |= EV_WRITE;
        }

        if ((os_ev->pfds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) {
            results[count].events |= EV_ERROR;
        }

        ++count;
    }

    return (int32_t)count;
}

#endif /* EV_USE_EPOLL */

static void os_ev_kill(Ev *_Nullable ev)
{
    if (ev == nullptr) {
        return;
    }

    OS_Ev *os_ev = (OS_Ev *)ev->user_data;
    const Memory *mem = os_ev->mem;

#ifdef EV_USE_EPOLL
    close(os_ev->epoll_fd);
    mem_delete(mem, os_ev->events);
#elif defined(_WIN32)
    closesocket(os_ev->helper_sock);
    mem_delete(mem, os_ev->h_events);
#else
    mem_delete(mem, os_ev->pfds);
#endif /* EV_USE_EPOLL */

    for (uint32_t i = 0; i < os_ev->regs_count; ++i) {
        mem_delete(mem, os_ev->regs[i]);
    }
    mem_delete(mem, os_ev->regs);
    mem_delete(mem, os_ev);
    mem_delete(mem, ev);
}

static const Ev_Funcs os_ev_funcs = {
    os_ev_add,
    os_ev_mod,
    os_ev_del,
    os_ev_run,
    os_ev_kill,
};

Ev *os_event_new(const Memory *mem, const Logger *log)
{
    OS_Ev *os_ev = (OS_Ev *)mem_alloc(mem, sizeof(OS_Ev));

    if (os_ev == nullptr) {
        return nullptr;
    }

    os_ev->mem = mem;
    os_ev->log = log;

#ifdef EV_USE_EPOLL
    os_ev->epoll_fd = epoll_create1(EPOLL_CLOEXEC);

    if (os_ev->epoll_fd == -1) {
        Net_Strerror error_buf;
        LOGGER_ERROR(log, "Failed to create epoll fd: %s", net_strerror(errno, &error_buf));
        mem_delete(mem, os_ev);
        return nullptr;
    }

#elif defined(_WIN32)
    // Create a dummy socket for AFD communication
    os_ev->helper_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (os_ev->helper_sock == INVALID_SOCKET) {
        Net_Strerror error_buf;
        LOGGER_ERROR(log, "Failed to create helper socket: %s", net_strerror(net_error(), &error_buf));
        mem_delete(mem, os_ev);
        return nullptr;
    }
#endif /* EV_USE_EPOLL */

    Ev *ev = (Ev *)mem_alloc(mem, sizeof(Ev));

    if (ev == nullptr) {
#ifdef EV_USE_EPOLL
        close(os_ev->epoll_fd);
#elif defined(_WIN32)
        closesocket(os_ev->helper_sock);
#endif /* EV_USE_EPOLL */
        mem_delete(mem, os_ev);
        return nullptr;
    }

    ev->funcs = &os_ev_funcs;
    ev->user_data = os_ev;

    LOGGER_DEBUG(log, "Created new os_event loop");

    return ev;
}