libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
io_int.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * Use of this source code is governed by the ISC License.
4 *
5 * src/inc/io_int.h
6 * Internal struct definition for the L1 backend implementations.
7 */
8
9#ifndef XTC_IO_INT_H
10#define XTC_IO_INT_H
11
12#include "xtc_io.h"
13#include <stdatomic.h>
14
15#if defined(XTC_IO_BACKEND_EPOLL)
16/* nothing to predefine; epoll keeps tags via epoll_data_t */
17#elif defined(XTC_IO_BACKEND_KQUEUE)
18/* kqueue's EV_ADD is idempotent and EV_DELETE silently ignores absent
19 * filters, so we keep an authoritative side-table of currently-
20 * registered fds to enforce M2's semantic contract. */
21#elif defined(XTC_IO_BACKEND_SOLARIS)
22/* illumos event ports are one-shot per association, so we maintain
23 * the same side-table as kqueue and re-arm on each delivery. */
24struct __xtc_solaris_reg {
25 int fd;
26 uint32_t interest;
27 void *tag;
28};
29#elif defined(XTC_IO_BACKEND_IOCP)
30/*
31 * Windows IOCP backend (round 2): native completion port plus the
32 * AFD poll fast path that turns a connected/listening socket's
33 * readiness into an IOCP completion.
34 *
35 * Each socket registration owns one AFD poll OVERLAPPED that is
36 * armed (submitted via NtDeviceIoControlFile(IOCTL_AFD_POLL)) into
37 * the kernel and re-armed after every completion -- level-triggered
38 * emulation matching the epoll/kqueue contract.
39 *
40 * OVERLAPPED OWNERSHIP RULE (the classic IOCP correctness invariant,
41 * enforced throughout io_iocp.c):
42 * The OVERLAPPED (ov) and the AFD_POLL_INFO (poll_info) embedded in
43 * a registration belong to the KERNEL from the moment the AFD poll
44 * is armed (NtDeviceIoControlFile returns STATUS_PENDING) until the
45 * matching completion is dequeued from the port by
46 * GetQueuedCompletionStatusEx. While armed (pending == 1) neither
47 * buffer may be freed or reused. Deregistering an armed socket
48 * issues NtCancelIoFileEx and marks the registration dead; the
49 * storage is only released once the (possibly canceled) completion
50 * is reaped. This is why the struct embeds ov/poll_info by value
51 * and the registration node is freed lazily, never inline with
52 * xtc_io_del_fd while a poll is in flight.
53 *
54 * The OVERLAPPED is the FIRST member so a completion's
55 * OVERLAPPED_ENTRY.lpOverlapped pointer can be cast straight back to
56 * the owning registration. Each registration is a separately
57 * heap-allocated node with a STABLE address (held in a pointer array,
58 * never an inline array), because the kernel-owned OVERLAPPED carries
59 * a back-pointer to its node for the whole time the poll is armed --
60 * a realloc or swap-remove of an inline array would dangle it.
61 */
62struct __xtc_iocp_reg {
63 struct __xtc_iocp_overlapped *ovp; /* OVERLAPPED + AFD_POLL_INFO (heap) */
64 int fd; /* the SOCKET as an int (Winsock handle) */
65 void *base; /* base socket HANDLE (SIO_BASE_HANDLE) */
66 uint32_t interest;
67 void *tag;
68 int pending; /* 1 while an AFD poll is armed in the kernel */
69 int dead; /* deregistered; awaiting terminal completion */
70};
71/* A file AIO (pread/pwrite) in flight on the IOCP backend. The
72 * file HANDLE is associated with the completion port, so the
73 * overlapped ReadFile/WriteFile completion is dequeued by
74 * GetQueuedCompletionStatusEx like any socket event -- no hEvent and
75 * no WaitForMultipleObjects. fsync has no async form on Windows
76 * (FlushFileBuffers is synchronous) and is offloaded. The OVERLAPPED
77 * is the FIRST member so the completion's lpOverlapped recovers the
78 * node; ownership follows the same kernel-owns-while-pending rule as
79 * the socket poll OVERLAPPED above. */
80struct __xtc_iocp_aio {
81 void *ov; /* OVERLAPPED * (heap; first field; owned here) */
82 void *aio; /* xtc_aio_t * awaiting completion */
83 void *fh; /* HANDLE: the file, for GetOverlappedResult */
84};
85#elif defined(XTC_IO_BACKEND_AIX)
86/* AIX pollset_* backend. Like solaris/kqueue, we maintain a
87 * side-table for duplicate detection and to map fd -> user tag
88 * (pollset itself doesn't carry udata). */
89struct __xtc_aix_reg {
90 int fd;
91 uint32_t interest;
92 void *tag;
93};
94#elif defined(XTC_IO_BACKEND_URING)
95#include <liburing.h>
96/*
97 * Per-fd state for the io_uring backend. The user_data passed to
98 * each POLL_ADD points at one of these. The fd_table maps fd ->
99 * uring_fd so we can find/cancel a registration on _del/_mod.
100 */
101struct __xtc_uring_fd {
102 int fd;
103 uint32_t interest;
104 void *tag;
105 int is_wakeup; /* 1 for the internal wakeup pipe */
106 int dead; /* deleted; awaiting terminal CQE before free */
107 struct __xtc_uring_fd *next; /* free-list / fd-list linkage */
108};
109#elif defined(XTC_IO_BACKEND_POLL)
110#include <poll.h>
111#elif defined(XTC_IO_BACKEND_SELECT)
112#include <sys/select.h>
113#elif defined(XTC_IO_BACKEND_SIM)
114/* Deterministic-simulation backend (DST): no kernel poller. Readiness
115 * and file-AIO completions come from a scripted in-process event store
116 * driven against the virtual clock; the wakeup is an in-process flag.
117 * See src/io/io_sim.c. */
118#else
119# error "M2 build expects XTC_IO_BACKEND_{POLL,EPOLL,URING,KQUEUE,IOCP,SOLARIS,AIX,SELECT,SIM} to be defined"
120#endif
121
122struct xtc_io {
123 int wakeup_rfd;
124 int wakeup_wfd;
125
126#if defined(XTC_IO_BACKEND_EPOLL)
127 int epfd;
128#elif defined(XTC_IO_BACKEND_KQUEUE)
129 int epfd; /* kqueue fd */
130 int *reg_fds; /* registered fd list */
131 int n_reg;
132 int cap_reg;
133#elif defined(XTC_IO_BACKEND_SOLARIS)
134 int epfd; /* event-port fd */
135 struct __xtc_solaris_reg *reg_fds;
136 int n_reg;
137 int cap_reg;
138#elif defined(XTC_IO_BACKEND_IOCP)
139 void *iocp; /* HANDLE: the completion port */
140 void *afd; /* HANDLE: \Device\Afd, port-associated */
141 _Atomic int wakeup_pending; /* 1 = a wakeup completion is queued */
142 struct __xtc_iocp_reg **reg_iocp; /* live registration nodes (stable) */
143 int n_reg;
144 int cap_reg;
145 struct __xtc_iocp_reg **dead_iocp; /* deregistered, awaiting completion */
146 int n_dead;
147 int cap_dead;
148 struct __xtc_iocp_aio *aio_pend; /* file AIOs in flight */
149 int n_aio;
150 int cap_aio;
151#elif defined(XTC_IO_BACKEND_AIX)
152 int ps; /* pollset_t */
153 void *reg_aix; /* struct __xtc_aix_reg * */
154 int n_reg;
155 int cap_reg;
156#elif defined(XTC_IO_BACKEND_URING)
157 struct io_uring ring;
158 struct __xtc_uring_fd *fds;
159 struct __xtc_uring_fd *zombies; /* deleted fds awaiting terminal CQE */
160#elif defined(XTC_IO_BACKEND_POLL)
161 struct pollfd *pfds;
162 void **tags;
163 int n;
164 int cap;
165#elif defined(XTC_IO_BACKEND_SELECT)
166 /* Parallel fd[], interest[], tag[] arrays. fd_set is built
167 * each poll() call from these. Capped at FD_SETSIZE. */
168 int *fds;
169 uint32_t *interests;
170 void **tags;
171 int n;
172 int cap;
173#elif defined(XTC_IO_BACKEND_SIM)
174 /* Registered fds (tag map) for readiness simulation, a scripted
175 * event queue ordered by virtual-time due, and an in-process
176 * wakeup flag. Defined in io_sim.c; opaque here. */
177 struct __xtc_sim_io *sim;
178#endif
179};
180
181#endif /* XTC_IO_INT_H */