From 4f1e7b0e9c885f07bd4bc577c5865ee035a05a10 Mon Sep 17 00:00:00 2001 From: server Date: Tue, 14 Apr 2026 07:03:44 +0200 Subject: [PATCH] net: encapsulate fdwatch backend metadata --- src/db/NetBase.cpp | 4 + src/game/main.cpp | 10 + src/libthecore/fdwatch.cpp | 1034 ++++++++++++++++++++++-------------- src/libthecore/fdwatch.h | 110 ++-- tests/smoke_auth.cpp | 20 + 5 files changed, 700 insertions(+), 478 deletions(-) diff --git a/src/db/NetBase.cpp b/src/db/NetBase.cpp index 9d51fe0..d37af00 100644 --- a/src/db/NetBase.cpp +++ b/src/db/NetBase.cpp @@ -37,6 +37,10 @@ bool CNetPoller::Create() return false; } + sys_log(0, "[STARTUP] fdwatch backend=%s descriptor_limit=%d", + fdwatch_backend_name(fdwatch_get_backend(m_fdWatcher)), + fdwatch_get_descriptor_limit(m_fdWatcher)); + return true; } diff --git a/src/game/main.cpp b/src/game/main.cpp index 90a2a89..76c4955 100644 --- a/src/game/main.cpp +++ b/src/game/main.cpp @@ -602,6 +602,16 @@ int start(int argc, char **argv) main_fdw = fdwatch_new(4096); + if (!main_fdw) + { + sys_err("fdwatch_new failed"); + return 0; + } + + sys_log(0, "[STARTUP] fdwatch backend=%s descriptor_limit=%d", + fdwatch_backend_name(fdwatch_get_backend(main_fdw)), + fdwatch_get_descriptor_limit(main_fdw)); + if ((tcp_socket = socket_tcp_bind(g_szPublicIP, mother_port)) == INVALID_SOCKET) { perror("socket_tcp_bind: tcp_socket"); diff --git a/src/libthecore/fdwatch.cpp b/src/libthecore/fdwatch.cpp index c717ff2..1213e31 100644 --- a/src/libthecore/fdwatch.cpp +++ b/src/libthecore/fdwatch.cpp @@ -2,482 +2,720 @@ #ifndef __USE_SELECT__ -LPFDWATCH fdwatch_new(int nfiles) +typedef struct kevent KEVENT; +typedef struct kevent * LPKEVENT; +typedef int KQUEUE; + +struct FdwatchKqueueState { - LPFDWATCH fdw; - int kq; + KQUEUE kq; + LPKEVENT kqevents; + int nkqevents; + LPKEVENT kqrevents; + int* fd_event_idx; + void** fd_data; + int* fd_rw; +}; - kq = kqueue(); - - if (kq == -1) - { - sys_err("%s", strerror(errno)); - return NULL; - } - - CREATE(fdw, FDWATCH, 1); - - fdw->kq = kq; - fdw->nfiles = nfiles; - fdw->nkqevents = 0; - - CREATE(fdw->kqevents, KEVENT, nfiles * 2); - CREATE(fdw->kqrevents, KEVENT, nfiles * 2); - CREATE(fdw->fd_event_idx, int, nfiles); - CREATE(fdw->fd_rw, int, nfiles); - CREATE(fdw->fd_data, void*, nfiles); - - return (fdw); -} - -void fdwatch_delete(LPFDWATCH fdw) -{ - free(fdw->fd_data); - free(fdw->fd_rw); - free(fdw->kqevents); - free(fdw->kqrevents); - free(fdw->fd_event_idx); - free(fdw); -} - -int fdwatch(LPFDWATCH fdw, struct timeval *timeout) -{ - int i, r; - struct timespec ts; - - if (fdw->nkqevents) - sys_log(2, "fdwatch: nkqevents %d", fdw->nkqevents); - - if (!timeout) - { - ts.tv_sec = 0; - ts.tv_nsec = 0; - - r = kevent(fdw->kq, fdw->kqevents, fdw->nkqevents, fdw->kqrevents, fdw->nfiles, &ts); - } - else - { - ts.tv_sec = timeout->tv_sec; - ts.tv_nsec = timeout->tv_usec; - - r = kevent(fdw->kq, fdw->kqevents, fdw->nkqevents, fdw->kqrevents, fdw->nfiles, &ts); - } - - fdw->nkqevents = 0; - - if (r == -1) - return -1; - - memset(fdw->fd_event_idx, 0, sizeof(int) * fdw->nfiles); - - for (i = 0; i < r; i++) - { - int fd = fdw->kqrevents[i].ident; - - if (fd >= fdw->nfiles) - sys_err("ident overflow %d nfiles: %d", fdw->kqrevents[i].ident, fdw->nfiles); - else - { - if (fdw->kqrevents[i].filter == EVFILT_WRITE) - fdw->fd_event_idx[fd] = i; - } - } - - return (r); -} - -void fdwatch_register(LPFDWATCH fdw, int flag, int fd, int rw) -{ - if (flag == EV_DELETE) - { - if (fdw->fd_rw[fd] & FDW_READ) - { - fdw->kqevents[fdw->nkqevents].ident = fd; - fdw->kqevents[fdw->nkqevents].flags = flag; - fdw->kqevents[fdw->nkqevents].filter = EVFILT_READ; - ++fdw->nkqevents; - } - - if (fdw->fd_rw[fd] & FDW_WRITE) - { - fdw->kqevents[fdw->nkqevents].ident = fd; - fdw->kqevents[fdw->nkqevents].flags = flag; - fdw->kqevents[fdw->nkqevents].filter = EVFILT_WRITE; - ++fdw->nkqevents; - } - } - else - { - fdw->kqevents[fdw->nkqevents].ident = fd; - fdw->kqevents[fdw->nkqevents].flags = flag; - fdw->kqevents[fdw->nkqevents].filter = (rw == FDW_READ) ? EVFILT_READ : EVFILT_WRITE; - - ++fdw->nkqevents; - } -} - -void fdwatch_clear_fd(LPFDWATCH fdw, socket_t fd) -{ - fdw->fd_data[fd] = NULL; - fdw->fd_rw[fd] = 0; -} - -void fdwatch_add_fd(LPFDWATCH fdw, socket_t fd, void * client_data, int rw, int oneshot) -{ - int flag; - - if (fd >= fdw->nfiles) - { - sys_err("fd overflow %d", fd); - return; - } - - if (fdw->fd_rw[fd] & rw) - return; - - fdw->fd_rw[fd] |= rw; - sys_log(2, "FDWATCH_fdw %p fd %d rw %d data %p", fdw, fd, rw, client_data); - - if (!oneshot) - flag = EV_ADD; - else - { - sys_log(2, "ADD ONESHOT fd_rw %d", fdw->fd_rw[fd]); - flag = EV_ADD | EV_ONESHOT; - fdw->fd_rw[fd] |= FDW_WRITE_ONESHOT; - } - - fdw->fd_data[fd] = client_data; - fdwatch_register(fdw, flag, fd, rw); -} - -void fdwatch_del_fd(LPFDWATCH fdw, socket_t fd) -{ - fdwatch_register(fdw, EV_DELETE, fd, 0); - fdwatch_clear_fd(fdw, fd); -} - -void fdwatch_clear_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx) -{ - assert(event_idx < fdw->nfiles * 2); - - if (fdw->kqrevents[event_idx].ident != fd) - return; - - fdw->kqrevents[event_idx].ident = 0; -} - -int fdwatch_check_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx) -{ - assert(event_idx < fdw->nfiles * 2); - - if (fdw->kqrevents[event_idx].ident != fd) - return 0; - - if (fdw->kqrevents[event_idx].flags & EV_ERROR) - return FDW_EOF; - - if (fdw->kqrevents[event_idx].flags & EV_EOF) - return FDW_EOF; - - if (fdw->kqrevents[event_idx].filter == EVFILT_READ) - { - if (fdw->fd_rw[fd] & FDW_READ) - return FDW_READ; - } - else if (fdw->kqrevents[event_idx].filter == EVFILT_WRITE) - { - if (fdw->fd_rw[fd] & FDW_WRITE) - { - if (fdw->fd_rw[fd] & FDW_WRITE_ONESHOT) - fdw->fd_rw[fd] &= ~FDW_WRITE; - - return FDW_WRITE; - } - } - else - sys_err("fdwatch_check_event: Unknown filter %d (descriptor %d)", fdw->kqrevents[event_idx].filter, fd); - - return 0; -} - -int fdwatch_get_ident(LPFDWATCH fdw, unsigned int event_idx) -{ - assert(event_idx < fdw->nfiles * 2); - return fdw->kqrevents[event_idx].ident; -} - -int fdwatch_get_buffer_size(LPFDWATCH fdw, socket_t fd) -{ - int event_idx = fdw->fd_event_idx[fd]; - - if (fdw->kqrevents[event_idx].filter == EVFILT_WRITE) - return fdw->kqrevents[event_idx].data; - - return 0; -} - -void * fdwatch_get_client_data(LPFDWATCH fdw, unsigned int event_idx) -{ - int fd; - - assert(event_idx < fdw->nfiles * 2); - - fd = fdw->kqrevents[event_idx].ident; - - if (fd >= fdw->nfiles) - return NULL; - - return (fdw->fd_data[fd]); -} -#else // ifndef __USE_SELECT__ +#else #ifdef OS_WINDOWS static int win32_init_refcount = 0; static bool win32_init() { - if (win32_init_refcount > 0) - { + if (win32_init_refcount > 0) + { + win32_init_refcount++; + return true; + } + + WORD wVersion = MAKEWORD(2, 0); + WSADATA wsaData; + + if (WSAStartup(wVersion, &wsaData) != 0) + return false; + win32_init_refcount++; return true; - } - - WORD wVersion = MAKEWORD(2, 0); - WSADATA wsaData; - - if (WSAStartup(wVersion, &wsaData) != 0) - return false; - - win32_init_refcount++; - return true; } static void win32_deinit() { - if (--win32_init_refcount <= 0) - WSACleanup(); + if (--win32_init_refcount <= 0) + WSACleanup(); } #endif -LPFDWATCH fdwatch_new(int nfiles) +struct FdwatchSelectState { - LPFDWATCH fdw; + fd_set rfd_set; + fd_set wfd_set; + socket_t* select_fds; + int* select_rfdidx; + int nselect_fds; + fd_set working_rfd_set; + fd_set working_wfd_set; + void** fd_data; + int* fd_rw; +}; -#ifdef OS_WINDOWS - if (!win32_init()) - return NULL; #endif - // nfiles value is limited to FD_SETSIZE (64) - CREATE(fdw, FDWATCH, 1); - fdw->nfiles = MIN(nfiles, FD_SETSIZE); - FD_ZERO(&fdw->rfd_set); - FD_ZERO(&fdw->wfd_set); - - CREATE(fdw->select_fds, socket_t, nfiles); - CREATE(fdw->select_rfdidx, int, nfiles); - - fdw->nselect_fds = 0; - - CREATE(fdw->fd_rw, int, nfiles); - CREATE(fdw->fd_data, void*, nfiles); - - return (fdw); -} - -void fdwatch_delete(LPFDWATCH fdw) +struct fdwatch { - free(fdw->fd_data); - free(fdw->fd_rw); - free(fdw->select_fds); - free(fdw->select_rfdidx); - free(fdw); + EFdwatchBackend backend; + int descriptor_limit; -#ifdef OS_WINDOWS - win32_deinit(); +#ifndef __USE_SELECT__ + FdwatchKqueueState state; +#else + FdwatchSelectState state; +#endif +}; + +static EFdwatchBackend fdwatch_default_backend() +{ +#ifndef __USE_SELECT__ + return FDWATCH_BACKEND_KQUEUE; +#else + return FDWATCH_BACKEND_SELECT; #endif } -static int fdwatch_get_fdidx(LPFDWATCH fdw, socket_t fd) { +#ifndef __USE_SELECT__ + +static LPFDWATCH fdwatch_new_kqueue(int nfiles) +{ + LPFDWATCH fdw; + int kq = kqueue(); + + if (kq == -1) + { + sys_err("%s", strerror(errno)); + return NULL; + } + + CREATE(fdw, FDWATCH, 1); + fdw->backend = FDWATCH_BACKEND_KQUEUE; + fdw->descriptor_limit = nfiles; + fdw->state.kq = kq; + fdw->state.nkqevents = 0; + + CREATE(fdw->state.kqevents, KEVENT, fdw->descriptor_limit * 2); + CREATE(fdw->state.kqrevents, KEVENT, fdw->descriptor_limit * 2); + CREATE(fdw->state.fd_event_idx, int, fdw->descriptor_limit); + CREATE(fdw->state.fd_rw, int, fdw->descriptor_limit); + CREATE(fdw->state.fd_data, void*, fdw->descriptor_limit); + + return fdw; +} + +static void fdwatch_delete_kqueue(LPFDWATCH fdw) +{ + free(fdw->state.fd_data); + free(fdw->state.fd_rw); + free(fdw->state.kqevents); + free(fdw->state.kqrevents); + free(fdw->state.fd_event_idx); + free(fdw); +} + +static int fdwatch_kqueue(LPFDWATCH fdw, struct timeval* timeout) +{ + auto& state = fdw->state; int i; - for (i = 0; i < fdw->nselect_fds; ++i) { - if (fdw->select_fds[i] == fd) { - return i; + int r; + struct timespec ts; + + if (state.nkqevents) + sys_log(2, "fdwatch: nkqevents %d", state.nkqevents); + + if (!timeout) + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + r = kevent(state.kq, state.kqevents, state.nkqevents, state.kqrevents, fdw->descriptor_limit, &ts); + } + else + { + ts.tv_sec = timeout->tv_sec; + ts.tv_nsec = timeout->tv_usec; + r = kevent(state.kq, state.kqevents, state.nkqevents, state.kqrevents, fdw->descriptor_limit, &ts); + } + + state.nkqevents = 0; + + if (r == -1) + return -1; + + memset(state.fd_event_idx, 0, sizeof(int) * fdw->descriptor_limit); + + for (i = 0; i < r; i++) + { + int fd = state.kqrevents[i].ident; + + if (fd >= fdw->descriptor_limit) + sys_err("ident overflow %d nfiles: %d", state.kqrevents[i].ident, fdw->descriptor_limit); + else if (state.kqrevents[i].filter == EVFILT_WRITE) + state.fd_event_idx[fd] = i; + } + + return r; +} + +static void fdwatch_register_kqueue(LPFDWATCH fdw, int flag, int fd, int rw) +{ + auto& state = fdw->state; + + if (flag == EV_DELETE) + { + if (state.fd_rw[fd] & FDW_READ) + { + state.kqevents[state.nkqevents].ident = fd; + state.kqevents[state.nkqevents].flags = flag; + state.kqevents[state.nkqevents].filter = EVFILT_READ; + ++state.nkqevents; + } + + if (state.fd_rw[fd] & FDW_WRITE) + { + state.kqevents[state.nkqevents].ident = fd; + state.kqevents[state.nkqevents].flags = flag; + state.kqevents[state.nkqevents].filter = EVFILT_WRITE; + ++state.nkqevents; } } + else + { + state.kqevents[state.nkqevents].ident = fd; + state.kqevents[state.nkqevents].flags = flag; + state.kqevents[state.nkqevents].filter = (rw == FDW_READ) ? EVFILT_READ : EVFILT_WRITE; + ++state.nkqevents; + } +} + +static void fdwatch_clear_fd_kqueue(LPFDWATCH fdw, socket_t fd) +{ + auto& state = fdw->state; + state.fd_data[fd] = NULL; + state.fd_rw[fd] = 0; +} + +static void fdwatch_add_fd_kqueue(LPFDWATCH fdw, socket_t fd, void* client_data, int rw, int oneshot) +{ + auto& state = fdw->state; + int flag; + + if (fd >= fdw->descriptor_limit) + { + sys_err("fd overflow %d", fd); + return; + } + + if (state.fd_rw[fd] & rw) + return; + + state.fd_rw[fd] |= rw; + sys_log(2, "FDWATCH_fdw %p fd %d rw %d data %p", fdw, fd, rw, client_data); + + if (!oneshot) + flag = EV_ADD; + else + { + sys_log(2, "ADD ONESHOT fd_rw %d", state.fd_rw[fd]); + flag = EV_ADD | EV_ONESHOT; + state.fd_rw[fd] |= FDW_WRITE_ONESHOT; + } + + state.fd_data[fd] = client_data; + fdwatch_register_kqueue(fdw, flag, fd, rw); +} + +static void fdwatch_del_fd_kqueue(LPFDWATCH fdw, socket_t fd) +{ + fdwatch_register_kqueue(fdw, EV_DELETE, fd, 0); + fdwatch_clear_fd_kqueue(fdw, fd); +} + +static void fdwatch_clear_event_kqueue(LPFDWATCH fdw, socket_t fd, unsigned int event_idx) +{ + auto& state = fdw->state; + assert(event_idx < static_cast(fdw->descriptor_limit * 2)); + + if (state.kqrevents[event_idx].ident != fd) + return; + + state.kqrevents[event_idx].ident = 0; +} + +static int fdwatch_check_event_kqueue(LPFDWATCH fdw, socket_t fd, unsigned int event_idx) +{ + auto& state = fdw->state; + assert(event_idx < static_cast(fdw->descriptor_limit * 2)); + + if (state.kqrevents[event_idx].ident != fd) + return 0; + + if (state.kqrevents[event_idx].flags & EV_ERROR) + return FDW_EOF; + + if (state.kqrevents[event_idx].flags & EV_EOF) + return FDW_EOF; + + if (state.kqrevents[event_idx].filter == EVFILT_READ) + { + if (state.fd_rw[fd] & FDW_READ) + return FDW_READ; + } + else if (state.kqrevents[event_idx].filter == EVFILT_WRITE) + { + if (state.fd_rw[fd] & FDW_WRITE) + { + if (state.fd_rw[fd] & FDW_WRITE_ONESHOT) + state.fd_rw[fd] &= ~FDW_WRITE; + + return FDW_WRITE; + } + } + else + sys_err("fdwatch_check_event: Unknown filter %d (descriptor %d)", state.kqrevents[event_idx].filter, fd); + + return 0; +} + +static int fdwatch_check_fd_kqueue(LPFDWATCH fdw, socket_t fd) +{ + (void)fdw; + (void)fd; + return 0; +} + +static int fdwatch_get_ident_kqueue(LPFDWATCH fdw, unsigned int event_idx) +{ + assert(event_idx < static_cast(fdw->descriptor_limit * 2)); + return fdw->state.kqrevents[event_idx].ident; +} + +static int fdwatch_get_buffer_size_kqueue(LPFDWATCH fdw, socket_t fd) +{ + auto& state = fdw->state; + int event_idx = state.fd_event_idx[fd]; + + if (state.kqrevents[event_idx].filter == EVFILT_WRITE) + return state.kqrevents[event_idx].data; + + return 0; +} + +static void* fdwatch_get_client_data_kqueue(LPFDWATCH fdw, unsigned int event_idx) +{ + auto& state = fdw->state; + int fd; + + assert(event_idx < static_cast(fdw->descriptor_limit * 2)); + fd = state.kqrevents[event_idx].ident; + + if (fd >= fdw->descriptor_limit) + return NULL; + + return state.fd_data[fd]; +} + +#else + +static LPFDWATCH fdwatch_new_select(int nfiles) +{ + LPFDWATCH fdw; + +#ifdef OS_WINDOWS + if (!win32_init()) + return NULL; +#endif + + CREATE(fdw, FDWATCH, 1); + fdw->backend = FDWATCH_BACKEND_SELECT; + fdw->descriptor_limit = MIN(nfiles, FD_SETSIZE); + + FD_ZERO(&fdw->state.rfd_set); + FD_ZERO(&fdw->state.wfd_set); + FD_ZERO(&fdw->state.working_rfd_set); + FD_ZERO(&fdw->state.working_wfd_set); + + CREATE(fdw->state.select_fds, socket_t, fdw->descriptor_limit); + CREATE(fdw->state.select_rfdidx, int, fdw->descriptor_limit); + CREATE(fdw->state.fd_rw, int, fdw->descriptor_limit); + CREATE(fdw->state.fd_data, void*, fdw->descriptor_limit); + + fdw->state.nselect_fds = 0; + return fdw; +} + +static void fdwatch_delete_select(LPFDWATCH fdw) +{ + free(fdw->state.fd_data); + free(fdw->state.fd_rw); + free(fdw->state.select_fds); + free(fdw->state.select_rfdidx); + free(fdw); + +#ifdef OS_WINDOWS + win32_deinit(); +#endif +} + +static int fdwatch_get_fdidx_select(LPFDWATCH fdw, socket_t fd) +{ + for (int i = 0; i < fdw->state.nselect_fds; ++i) + { + if (fdw->state.select_fds[i] == fd) + return i; + } + return -1; } -static int fdwatch_get_maxfd(LPFDWATCH fdw) +static int fdwatch_get_maxfd_select(LPFDWATCH fdw) { - int i; socket_t max_fd = 0; - for (i = 0; i < fdw->nselect_fds; ++i) + for (int i = 0; i < fdw->state.nselect_fds; ++i) { - if (fdw->select_fds[i] > max_fd) - max_fd = fdw->select_fds[i]; + if (fdw->state.select_fds[i] > max_fd) + max_fd = fdw->state.select_fds[i]; } return static_cast(max_fd) + 1; } -void fdwatch_add_fd(LPFDWATCH fdw, socket_t fd, void* client_data, int rw, int oneshot) +static int fdwatch_check_fd_select(LPFDWATCH fdw, socket_t fd); + +static void fdwatch_clear_fd_select(LPFDWATCH fdw, socket_t fd) { - int idx = fdwatch_get_fdidx(fdw, fd); - if (idx < 0) { - if (fdw->nselect_fds >= fdw->nfiles) { + int idx = fdwatch_get_fdidx_select(fdw, fd); + + if (idx < 0) + return; + + fdw->state.fd_data[idx] = NULL; + fdw->state.fd_rw[idx] = 0; + FD_CLR(fd, &fdw->state.rfd_set); + FD_CLR(fd, &fdw->state.wfd_set); + FD_CLR(fd, &fdw->state.working_rfd_set); + FD_CLR(fd, &fdw->state.working_wfd_set); +} + +static void fdwatch_add_fd_select(LPFDWATCH fdw, socket_t fd, void* client_data, int rw, int oneshot) +{ + int idx = fdwatch_get_fdidx_select(fdw, fd); + + if (idx < 0) + { + if (fdw->state.nselect_fds >= fdw->descriptor_limit) return; - } - idx = fdw->nselect_fds; - fdw->select_fds[fdw->nselect_fds++] = fd; - fdw->fd_rw[idx] = 0; + + idx = fdw->state.nselect_fds; + fdw->state.select_fds[fdw->state.nselect_fds++] = fd; + fdw->state.fd_rw[idx] = 0; } - fdw->fd_rw[idx] |= rw; + fdw->state.fd_rw[idx] |= rw; + if (oneshot && (rw & FDW_WRITE)) - fdw->fd_rw[idx] |= FDW_WRITE_ONESHOT; + fdw->state.fd_rw[idx] |= FDW_WRITE_ONESHOT; - fdw->fd_data[idx] = client_data; + fdw->state.fd_data[idx] = client_data; - if (rw & FDW_READ) - FD_SET(fd, &fdw->rfd_set); + if (rw & FDW_READ) + FD_SET(fd, &fdw->state.rfd_set); - if (rw & FDW_WRITE) - FD_SET(fd, &fdw->wfd_set); + if (rw & FDW_WRITE) + FD_SET(fd, &fdw->state.wfd_set); } -void fdwatch_del_fd(LPFDWATCH fdw, socket_t fd) +static void fdwatch_del_fd_select(LPFDWATCH fdw, socket_t fd) { - if (fdw->nselect_fds <= 0) { + if (fdw->state.nselect_fds <= 0) return; - } - int idx = fdwatch_get_fdidx(fdw, fd); - if (idx < 0) { + + int idx = fdwatch_get_fdidx_select(fdw, fd); + + if (idx < 0) return; - } - --fdw->nselect_fds; + --fdw->state.nselect_fds; - fdw->select_fds[idx] = fdw->select_fds[fdw->nselect_fds]; - fdw->fd_data[idx] = fdw->fd_data[fdw->nselect_fds]; - fdw->fd_rw[idx] = fdw->fd_rw[fdw->nselect_fds]; + fdw->state.select_fds[idx] = fdw->state.select_fds[fdw->state.nselect_fds]; + fdw->state.fd_data[idx] = fdw->state.fd_data[fdw->state.nselect_fds]; + fdw->state.fd_rw[idx] = fdw->state.fd_rw[fdw->state.nselect_fds]; - FD_CLR(fd, &fdw->rfd_set); - FD_CLR(fd, &fdw->wfd_set); + FD_CLR(fd, &fdw->state.rfd_set); + FD_CLR(fd, &fdw->state.wfd_set); + FD_CLR(fd, &fdw->state.working_rfd_set); + FD_CLR(fd, &fdw->state.working_wfd_set); } -int fdwatch(LPFDWATCH fdw, struct timeval *timeout) +static int fdwatch_select(LPFDWATCH fdw, struct timeval* timeout) { - int r, i, event_idx; - int max_fd = fdwatch_get_maxfd(fdw); - struct timeval tv; + int r; + int event_idx = 0; + int max_fd = fdwatch_get_maxfd_select(fdw); + struct timeval tv; - fdw->working_rfd_set = fdw->rfd_set; - fdw->working_wfd_set = fdw->wfd_set; + fdw->state.working_rfd_set = fdw->state.rfd_set; + fdw->state.working_wfd_set = fdw->state.wfd_set; - if (!timeout) - { - tv.tv_sec = 0; - tv.tv_usec = 0; - r = select(max_fd, &fdw->working_rfd_set, &fdw->working_wfd_set, (fd_set*) 0, &tv); - } - else - { - tv = *timeout; - r = select(max_fd, &fdw->working_rfd_set, &fdw->working_wfd_set, (fd_set*) 0, &tv); - } + if (!timeout) + { + tv.tv_sec = 0; + tv.tv_usec = 0; + r = select(max_fd, &fdw->state.working_rfd_set, &fdw->state.working_wfd_set, (fd_set*)0, &tv); + } + else + { + tv = *timeout; + r = select(max_fd, &fdw->state.working_rfd_set, &fdw->state.working_wfd_set, (fd_set*)0, &tv); + } - if (r == -1) - return -1; + if (r == -1) + return -1; - event_idx = 0; + for (int i = 0; i < fdw->state.nselect_fds; ++i) + { + if (fdwatch_check_fd_select(fdw, fdw->state.select_fds[i])) + fdw->state.select_rfdidx[event_idx++] = i; + } - for (i = 0; i < fdw->nselect_fds; ++i) - { - if (fdwatch_check_fd(fdw, fdw->select_fds[i])) - fdw->select_rfdidx[event_idx++] = i; - } + return event_idx; +} - return event_idx; +static int fdwatch_check_fd_select(LPFDWATCH fdw, socket_t fd) +{ + int idx = fdwatch_get_fdidx_select(fdw, fd); + + if (idx < 0) + return 0; + + int result = 0; + + if ((fdw->state.fd_rw[idx] & FDW_READ) && FD_ISSET(fd, &fdw->state.working_rfd_set)) + result |= FDW_READ; + + if ((fdw->state.fd_rw[idx] & FDW_WRITE) && FD_ISSET(fd, &fdw->state.working_wfd_set)) + result |= FDW_WRITE; + + return result; +} + +static void* fdwatch_get_client_data_select(LPFDWATCH fdw, unsigned int event_idx) +{ + int idx = fdw->state.select_rfdidx[event_idx]; + + if (idx < 0 || fdw->state.nselect_fds <= idx) + return NULL; + + return fdw->state.fd_data[idx]; +} + +static int fdwatch_get_ident_select(LPFDWATCH fdw, unsigned int event_idx) +{ + int idx = fdw->state.select_rfdidx[event_idx]; + + if (idx < 0 || fdw->state.nselect_fds <= idx) + return 0; + + return static_cast(fdw->state.select_fds[idx]); +} + +static void fdwatch_clear_event_select(LPFDWATCH fdw, socket_t fd, unsigned int event_idx) +{ + int idx = fdw->state.select_rfdidx[event_idx]; + + if (idx < 0 || fdw->state.nselect_fds <= idx) + return; + + socket_t rfd = fdw->state.select_fds[idx]; + + if (fd != rfd) + return; + + FD_CLR(fd, &fdw->state.working_rfd_set); + FD_CLR(fd, &fdw->state.working_wfd_set); +} + +static int fdwatch_check_event_select(LPFDWATCH fdw, socket_t fd, unsigned int event_idx) +{ + int idx = fdw->state.select_rfdidx[event_idx]; + + if (idx < 0 || fdw->state.nselect_fds <= idx) + return 0; + + socket_t rfd = fdw->state.select_fds[idx]; + + if (fd != rfd) + return 0; + + int result = fdwatch_check_fd_select(fdw, fd); + + if (result & FDW_READ) + return FDW_READ; + + if (result & FDW_WRITE) + { + if (fdw->state.fd_rw[idx] & FDW_WRITE_ONESHOT) + { + fdw->state.fd_rw[idx] &= ~(FDW_WRITE | FDW_WRITE_ONESHOT); + FD_CLR(fd, &fdw->state.wfd_set); + FD_CLR(fd, &fdw->state.working_wfd_set); + } + + return FDW_WRITE; + } + + return 0; +} + +static int fdwatch_get_buffer_size_select(LPFDWATCH fdw, socket_t fd) +{ + (void)fdw; + (void)fd; + return INT_MAX; // XXX TODO +} + +#endif +EFdwatchBackend fdwatch_get_backend(LPFDWATCH fdw) +{ + if (!fdw) + return fdwatch_default_backend(); + + return fdw->backend; +} + +const char* fdwatch_backend_name(EFdwatchBackend backend) +{ + switch (backend) + { + case FDWATCH_BACKEND_KQUEUE: + return "kqueue"; + case FDWATCH_BACKEND_SELECT: + return "select"; + default: + return "unknown"; + } +} + +int fdwatch_get_descriptor_limit(LPFDWATCH fdw) +{ + if (!fdw) + return 0; + + return fdw->descriptor_limit; +} + +LPFDWATCH fdwatch_new(int nfiles) +{ +#ifndef __USE_SELECT__ + return fdwatch_new_kqueue(nfiles); +#else + return fdwatch_new_select(nfiles); +#endif +} + +void fdwatch_clear_fd(LPFDWATCH fdw, socket_t fd) +{ +#ifndef __USE_SELECT__ + fdwatch_clear_fd_kqueue(fdw, fd); +#else + fdwatch_clear_fd_select(fdw, fd); +#endif +} + +void fdwatch_delete(LPFDWATCH fdw) +{ +#ifndef __USE_SELECT__ + fdwatch_delete_kqueue(fdw); +#else + fdwatch_delete_select(fdw); +#endif } int fdwatch_check_fd(LPFDWATCH fdw, socket_t fd) { - int idx = fdwatch_get_fdidx(fdw, fd); - if (idx < 0) { - return 0; - } - int result = 0; - if ((fdw->fd_rw[idx] & FDW_READ) && FD_ISSET(fd, &fdw->working_rfd_set)) { - result |= FDW_READ; - } - if ((fdw->fd_rw[idx] & FDW_WRITE) && FD_ISSET(fd, &fdw->working_wfd_set)) { - result |= FDW_WRITE; - } - return result; -} - -void * fdwatch_get_client_data(LPFDWATCH fdw, unsigned int event_idx) -{ - int idx = fdw->select_rfdidx[event_idx]; - if (idx < 0 || fdw->nselect_fds <= idx) { - return NULL; - } - return fdw->fd_data[idx]; -} - -int fdwatch_get_ident(LPFDWATCH fdw, unsigned int event_idx) -{ - int idx = fdw->select_rfdidx[event_idx]; - if (idx < 0 || fdw->nselect_fds <= idx) { - return 0; - } - return (int)fdw->select_fds[idx]; -} - -void fdwatch_clear_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx) -{ - int idx = fdw->select_rfdidx[event_idx]; - if (idx < 0 || fdw->nselect_fds <= idx) { - return; - } - socket_t rfd = fdw->select_fds[idx]; - if (fd != rfd) { - return; - } - FD_CLR(fd, &fdw->working_rfd_set); - FD_CLR(fd, &fdw->working_wfd_set); +#ifndef __USE_SELECT__ + return fdwatch_check_fd_kqueue(fdw, fd); +#else + return fdwatch_check_fd_select(fdw, fd); +#endif } int fdwatch_check_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx) { - int idx = fdw->select_rfdidx[event_idx]; - if (idx < 0 || fdw->nselect_fds <= idx) { - return 0; - } - socket_t rfd = fdw->select_fds[idx]; - if (fd != rfd) { - return 0; - } - int result = fdwatch_check_fd(fdw, fd); - if (result & FDW_READ) { - return FDW_READ; - } else if (result & FDW_WRITE) { - if (fdw->fd_rw[idx] & FDW_WRITE_ONESHOT) { - fdw->fd_rw[idx] &= ~(FDW_WRITE | FDW_WRITE_ONESHOT); - FD_CLR(fd, &fdw->wfd_set); - FD_CLR(fd, &fdw->working_wfd_set); - } - return FDW_WRITE; - } - return 0; +#ifndef __USE_SELECT__ + return fdwatch_check_event_kqueue(fdw, fd, event_idx); +#else + return fdwatch_check_event_select(fdw, fd, event_idx); +#endif +} + +void fdwatch_clear_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx) +{ +#ifndef __USE_SELECT__ + fdwatch_clear_event_kqueue(fdw, fd, event_idx); +#else + fdwatch_clear_event_select(fdw, fd, event_idx); +#endif +} + +void fdwatch_add_fd(LPFDWATCH fdw, socket_t fd, void* client_data, int rw, int oneshot) +{ +#ifndef __USE_SELECT__ + fdwatch_add_fd_kqueue(fdw, fd, client_data, rw, oneshot); +#else + fdwatch_add_fd_select(fdw, fd, client_data, rw, oneshot); +#endif +} + +int fdwatch(LPFDWATCH fdw, struct timeval* timeout) +{ +#ifndef __USE_SELECT__ + return fdwatch_kqueue(fdw, timeout); +#else + return fdwatch_select(fdw, timeout); +#endif +} + +void* fdwatch_get_client_data(LPFDWATCH fdw, unsigned int event_idx) +{ +#ifndef __USE_SELECT__ + return fdwatch_get_client_data_kqueue(fdw, event_idx); +#else + return fdwatch_get_client_data_select(fdw, event_idx); +#endif +} + +void fdwatch_del_fd(LPFDWATCH fdw, socket_t fd) +{ +#ifndef __USE_SELECT__ + fdwatch_del_fd_kqueue(fdw, fd); +#else + fdwatch_del_fd_select(fdw, fd); +#endif } int fdwatch_get_buffer_size(LPFDWATCH fdw, socket_t fd) { - return INT_MAX; // XXX TODO +#ifndef __USE_SELECT__ + return fdwatch_get_buffer_size_kqueue(fdw, fd); +#else + return fdwatch_get_buffer_size_select(fdw, fd); +#endif } +int fdwatch_get_ident(LPFDWATCH fdw, unsigned int event_idx) +{ +#ifndef __USE_SELECT__ + return fdwatch_get_ident_kqueue(fdw, event_idx); +#else + return fdwatch_get_ident_select(fdw, event_idx); #endif +} diff --git a/src/libthecore/fdwatch.h b/src/libthecore/fdwatch.h index 39829a9..7075a78 100644 --- a/src/libthecore/fdwatch.h +++ b/src/libthecore/fdwatch.h @@ -1,85 +1,35 @@ #pragma once -#ifndef __USE_SELECT__ +typedef struct fdwatch FDWATCH; +typedef struct fdwatch * LPFDWATCH; - typedef struct fdwatch FDWATCH; - typedef struct fdwatch * LPFDWATCH; +enum EFdwatch +{ + FDW_NONE = 0, + FDW_READ = 1, + FDW_WRITE = 2, + FDW_WRITE_ONESHOT = 4, + FDW_EOF = 8, +}; - enum EFdwatch - { - FDW_NONE = 0, - FDW_READ = 1, - FDW_WRITE = 2, - FDW_WRITE_ONESHOT = 4, - FDW_EOF = 8, - }; - - typedef struct kevent KEVENT; - typedef struct kevent * LPKEVENT; - typedef int KQUEUE; - - struct fdwatch - { - KQUEUE kq; - - int nfiles; - - LPKEVENT kqevents; - int nkqevents; - - LPKEVENT kqrevents; - int * fd_event_idx; - - void ** fd_data; - int * fd_rw; - }; - -#else - - typedef struct fdwatch FDWATCH; - typedef struct fdwatch * LPFDWATCH; - - enum EFdwatch - { - FDW_NONE = 0, - FDW_READ = 1, - FDW_WRITE = 2, - FDW_WRITE_ONESHOT = 4, - FDW_EOF = 8, - }; - - struct fdwatch - { - fd_set rfd_set; - fd_set wfd_set; - - socket_t* select_fds; - int* select_rfdidx; - - int nselect_fds; - - fd_set working_rfd_set; - fd_set working_wfd_set; - - int nfiles; - - void** fd_data; - int* fd_rw; - }; - -#endif // WIN32 - - -LPFDWATCH fdwatch_new(int nfiles); -void fdwatch_clear_fd(LPFDWATCH fdw, socket_t fd); -void fdwatch_delete(LPFDWATCH fdw); -int fdwatch_check_fd(LPFDWATCH fdw, socket_t fd); -int fdwatch_check_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx); -void fdwatch_clear_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx); -void fdwatch_add_fd(LPFDWATCH fdw, socket_t fd, void* client_data, int rw, int oneshot); -int fdwatch(LPFDWATCH fdw, struct timeval *timeout); -void * fdwatch_get_client_data(LPFDWATCH fdw, unsigned int event_idx); -void fdwatch_del_fd(LPFDWATCH fdw, socket_t fd); -int fdwatch_get_buffer_size(LPFDWATCH fdw, socket_t fd); -int fdwatch_get_ident(LPFDWATCH fdw, unsigned int event_idx); +enum EFdwatchBackend +{ + FDWATCH_BACKEND_KQUEUE = 0, + FDWATCH_BACKEND_SELECT = 1, +}; +LPFDWATCH fdwatch_new(int nfiles); +void fdwatch_clear_fd(LPFDWATCH fdw, socket_t fd); +void fdwatch_delete(LPFDWATCH fdw); +int fdwatch_check_fd(LPFDWATCH fdw, socket_t fd); +int fdwatch_check_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx); +void fdwatch_clear_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx); +void fdwatch_add_fd(LPFDWATCH fdw, socket_t fd, void* client_data, int rw, int oneshot); +int fdwatch(LPFDWATCH fdw, struct timeval *timeout); +void * fdwatch_get_client_data(LPFDWATCH fdw, unsigned int event_idx); +void fdwatch_del_fd(LPFDWATCH fdw, socket_t fd); +int fdwatch_get_buffer_size(LPFDWATCH fdw, socket_t fd); +int fdwatch_get_ident(LPFDWATCH fdw, unsigned int event_idx); +EFdwatchBackend fdwatch_get_backend(LPFDWATCH fdw); +const char * fdwatch_backend_name(EFdwatchBackend backend); +int fdwatch_get_descriptor_limit(LPFDWATCH fdw); diff --git a/tests/smoke_auth.cpp b/tests/smoke_auth.cpp index 040795f..c93343c 100644 --- a/tests/smoke_auth.cpp +++ b/tests/smoke_auth.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -289,6 +290,24 @@ void TestFdwatchReadAndOneshotWrite() close(sockets[0]); close(sockets[1]); } + +void TestFdwatchBackendMetadata() +{ + LPFDWATCH fdw = fdwatch_new(4096); + Expect(fdw != nullptr, "fdwatch_new for backend metadata failed"); + +#ifdef __USE_SELECT__ + Expect(fdwatch_get_backend(fdw) == FDWATCH_BACKEND_SELECT, "Expected select backend"); + Expect(std::strcmp(fdwatch_backend_name(fdwatch_get_backend(fdw)), "select") == 0, "Unexpected select backend name"); + Expect(fdwatch_get_descriptor_limit(fdw) == std::min(4096, static_cast(FD_SETSIZE)), "Unexpected select descriptor limit"); +#else + Expect(fdwatch_get_backend(fdw) == FDWATCH_BACKEND_KQUEUE, "Expected kqueue backend"); + Expect(std::strcmp(fdwatch_backend_name(fdwatch_get_backend(fdw)), "kqueue") == 0, "Unexpected kqueue backend name"); + Expect(fdwatch_get_descriptor_limit(fdw) == 4096, "Unexpected kqueue descriptor limit"); +#endif + + fdwatch_delete(fdw); +} } int main() @@ -298,6 +317,7 @@ int main() TestPacketLayouts(); TestSecureCipherRoundTrip(); TestSocketAuthWireFlow(); + TestFdwatchBackendMetadata(); TestFdwatchReadAndOneshotWrite(); std::cout << "metin smoke tests passed\n"; return 0;