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 int64_t armed_at_ns; /* __os_clock_mono() when this arm was issued;
71 * drives the bounded re-poll workaround for
72 * the AFD async-completion bug (see io_iocp.c
73 * __xtc_iocp_repoll_sweep) */
74};
75/* A file AIO (pread/pwrite) in flight on the IOCP backend. The
76 * file HANDLE is associated with the completion port, so the
77 * overlapped ReadFile/WriteFile completion is dequeued by
78 * GetQueuedCompletionStatusEx like any socket event -- no hEvent and
79 * no WaitForMultipleObjects. fsync has no async form on Windows
80 * (FlushFileBuffers is synchronous) and is offloaded. The OVERLAPPED
81 * is the FIRST member so the completion's lpOverlapped recovers the
82 * node; ownership follows the same kernel-owns-while-pending rule as
83 * the socket poll OVERLAPPED above. */
84struct __xtc_iocp_aio {
85 void *ov; /* OVERLAPPED * (heap; first field; owned here) */
86 void *aio; /* xtc_aio_t * awaiting completion */
87 void *fh; /* HANDLE: the file, for GetOverlappedResult */
88};
89#elif defined(XTC_IO_BACKEND_AIX)
90/* AIX pollset_* backend. Like solaris/kqueue, we maintain a
91 * side-table for duplicate detection and to map fd -> user tag
92 * (pollset itself doesn't carry udata). */
93struct __xtc_aix_reg {
94 int fd;
95 uint32_t interest;
96 void *tag;
97};
98#elif defined(XTC_IO_BACKEND_URING)
99#include <liburing.h>
100/*
101 * Per-fd state for the io_uring backend. The user_data passed to
102 * each POLL_ADD points at one of these. The fd_table maps fd ->
103 * uring_fd so we can find/cancel a registration on _del/_mod.
104 */
105struct __xtc_uring_fd {
106 int fd;
107 uint32_t interest;
108 void *tag;
109 int is_wakeup; /* 1 for the internal wakeup pipe */
110 int dead; /* deleted; awaiting terminal CQE before free */
111 struct __xtc_uring_fd *next; /* free-list / fd-list linkage */
112};
113#elif defined(XTC_IO_BACKEND_POLL)
114#include <poll.h>
115#elif defined(XTC_IO_BACKEND_SELECT)
116#include <sys/select.h>
117#elif defined(XTC_IO_BACKEND_SIM)
118/* Deterministic-simulation backend (DST): no kernel poller. Readiness
119 * and file-AIO completions come from a scripted in-process event store
120 * driven against the virtual clock; the wakeup is an in-process flag.
121 * See src/io/io_sim.c. */
122#else
123# error "M2 build expects XTC_IO_BACKEND_{POLL,EPOLL,URING,KQUEUE,IOCP,SOLARIS,AIX,SELECT,SIM} to be defined"
124#endif
125
126struct xtc_io {
127 int wakeup_rfd;
128 int wakeup_wfd;
129
130#if defined(XTC_IO_BACKEND_EPOLL)
131 int epfd;
132#elif defined(XTC_IO_BACKEND_KQUEUE)
133 int epfd; /* kqueue fd */
134 int *reg_fds; /* registered fd list */
135 int n_reg;
136 int cap_reg;
137#elif defined(XTC_IO_BACKEND_SOLARIS)
138 int epfd; /* event-port fd */
139 struct __xtc_solaris_reg *reg_fds;
140 int n_reg;
141 int cap_reg;
142#elif defined(XTC_IO_BACKEND_IOCP)
143 void *iocp; /* HANDLE: the completion port */
144 void *afd; /* HANDLE: \Device\Afd, port-associated */
145 _Atomic int wakeup_pending; /* 1 = a wakeup completion is queued */
146 struct __xtc_iocp_reg **reg_iocp; /* live registration nodes (stable) */
147 int n_reg;
148 int cap_reg;
149 struct __xtc_iocp_reg **dead_iocp; /* deregistered, awaiting completion */
150 int n_dead;
151 int cap_dead;
152 struct __xtc_iocp_aio *aio_pend; /* file AIOs in flight */
153 int n_aio;
154 int cap_aio;
155#elif defined(XTC_IO_BACKEND_AIX)
156 int ps; /* pollset_t */
157 void *reg_aix; /* struct __xtc_aix_reg * */
158 int n_reg;
159 int cap_reg;
160#elif defined(XTC_IO_BACKEND_URING)
161 struct io_uring ring;
162 struct __xtc_uring_fd *fds;
163 struct __xtc_uring_fd *zombies; /* deleted fds awaiting terminal CQE */
164#elif defined(XTC_IO_BACKEND_POLL)
165 struct pollfd *pfds;
166 void **tags;
167 int n;
168 int cap;
169#elif defined(XTC_IO_BACKEND_SELECT)
170 /* Parallel fd[], interest[], tag[] arrays. fd_set is built
171 * each poll() call from these. Capped at FD_SETSIZE. */
172 int *fds;
173 uint32_t *interests;
174 void **tags;
175 int n;
176 int cap;
177#elif defined(XTC_IO_BACKEND_SIM)
178 /* Registered fds (tag map) for readiness simulation, a scripted
179 * event queue ordered by virtual-time due, and an in-process
180 * wakeup flag. Defined in io_sim.c; opaque here. */
181 struct __xtc_sim_io *sim;
182#endif
183};
184
185#endif /* XTC_IO_INT_H */