use super::{Future, Pin};
use std::net::SocketAddr;
use std::os::windows::io::{AsRawSocket, FromRawSocket, RawSocket};
use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicPtr, AtomicUsize, Ordering, fence};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use crate::io::trace::{io_trace, trace_now_us};
use crate::lockfree::{SpscQueue, TreiberStack};
use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::Networking::WinSock::{
AF_INET, AF_INET6, IN_ADDR, IN_ADDR_0, IN6_ADDR, IN6_ADDR_0, INVALID_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT, SO_UPDATE_CONNECT_CONTEXT, SOCK_STREAM, SOCKADDR, SOCKADDR_IN,
SOCKADDR_IN6, SOCKADDR_IN6_0, SOCKADDR_STORAGE, SOCKET, SOL_SOCKET, WSA_FLAG_OVERLAPPED,
WSA_IO_PENDING, WSABUF, WSAEINVAL, WSAGetLastError, WSAGetOverlappedResult, WSAIoctl, WSARecv,
WSARecvFrom, WSASend, WSASendTo, WSASocketW, bind, closesocket, setsockopt,
};
use windows_sys::Win32::System::IO::{
CreateIoCompletionPort, GetQueuedCompletionStatusEx, OVERLAPPED, OVERLAPPED_ENTRY,
PostQueuedCompletionStatus,
};
#[repr(align(64))]
struct WakerSlot {
waker_data: AtomicPtr<()>,
waker_vtable: AtomicPtr<RawWakerVTable>,
waker_lock: AtomicBool,
result: AtomicI32,
completed: AtomicBool,
dropped: AtomicBool,
origin_socket: AtomicUsize,
}
impl WakerSlot {
#[inline(always)]
fn lock_waker(&self) {
while self
.waker_lock
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
}
}
#[inline(always)]
fn unlock_waker(&self) {
self.waker_lock.store(false, Ordering::Release);
}
}
#[repr(align(64))]
struct WaitSlot {
waker_data: AtomicPtr<()>,
waker_vtable: AtomicPtr<RawWakerVTable>,
}
#[inline(always)]
fn wake_next_waiting_fiber(state: &WorkerState) {
if let Some(wait_idx) = state.waiting_queue.pop() {
let wait_slot = &state.wait_slots[wait_idx as usize];
let data = wait_slot
.waker_data
.swap(std::ptr::null_mut(), Ordering::Relaxed);
let vtable = wait_slot
.waker_vtable
.swap(std::ptr::null_mut(), Ordering::Relaxed);
state.free_wait_slots.push(wait_idx);
if !data.is_null() && !vtable.is_null() {
let raw = RawWaker::new(data.cast_const(), unsafe { &*vtable });
let w = unsafe { Waker::from_raw(raw) };
w.wake();
}
}
}
thread_local! {
static THREAD_ID: usize = {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
NEXT_ID.fetch_add(1, Ordering::Relaxed)
};
}
fn get_local_thread_id() -> usize {
THREAD_ID.with(|id| *id)
}
pub(super) struct GlobalConfig {
pub(super) workers: usize,
}
pub(super) static GLOBAL_CONFIG: OnceLock<GlobalConfig> = OnceLock::new();
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OpCode {
Read,
Write,
Accept,
Connect,
SendTo,
RecvFrom,
}
const WAKE_KEY: usize = usize::MAX;
#[repr(C)]
struct IoOverlapped {
overlapped: OVERLAPPED,
slot_idx: usize,
issuing_socket: usize,
accept_socket: usize,
accept_addr_buf: [u8; 288],
}
enum IoRequest {
Read {
socket: usize,
buf_ptr: *mut u8,
len: usize,
slot_idx: usize,
},
Write {
socket: usize,
buf_ptr: *const u8,
len: usize,
slot_idx: usize,
},
Accept {
listen_socket: usize,
accept_socket: usize,
slot_idx: usize,
},
Connect {
socket: usize,
addr: SOCKADDR_STORAGE,
addr_len: i32,
slot_idx: usize,
},
SendTo {
socket: usize,
buf_ptr: *const u8,
len: usize,
addr: SOCKADDR_STORAGE,
addr_len: i32,
slot_idx: usize,
},
RecvFrom {
socket: usize,
buf_ptr: *mut u8,
len: usize,
from_ptr: *mut SOCKADDR_STORAGE,
from_len_ptr: *mut i32,
slot_idx: usize,
},
Cancel { slot_idx: usize },
}
unsafe impl Send for IoRequest {}
type AcceptExFn = unsafe extern "system" fn(
SOCKET,
SOCKET,
*mut core::ffi::c_void,
u32,
u32,
u32,
*mut u32,
*mut OVERLAPPED,
) -> i32;
type ConnectExFn = unsafe extern "system" fn(
SOCKET,
*const SOCKADDR,
i32,
*mut core::ffi::c_void,
u32,
*mut u32,
*mut OVERLAPPED,
) -> i32;
const SIO_GET_EXTENSION_FUNCTION_POINTER: u32 = 0xC800_0006;
const WSAID_ACCEPTEX: windows_sys::core::GUID = windows_sys::core::GUID {
data1: 0xb536_7df1,
data2: 0xcbac,
data3: 0x11cf,
data4: [0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92],
};
const WSAID_CONNECTEX: windows_sys::core::GUID = windows_sys::core::GUID {
data1: 0x25a2_07b9,
data2: 0xddf3,
data3: 0x4660,
data4: [0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e],
};
fn get_extension_fn<F: Copy>(socket: SOCKET, guid: &windows_sys::core::GUID) -> std::io::Result<F> {
assert_eq!(std::mem::size_of::<F>(), std::mem::size_of::<usize>());
let mut fn_ptr: usize = 0;
let mut bytes_returned: u32 = 0;
let res = unsafe {
WSAIoctl(
socket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
std::ptr::from_ref(guid).cast::<core::ffi::c_void>(),
std::mem::size_of::<windows_sys::core::GUID>() as u32,
(&raw mut fn_ptr).cast::<core::ffi::c_void>(),
std::mem::size_of::<usize>() as u32,
&raw mut bytes_returned,
std::ptr::null_mut(),
None,
)
};
if res != 0 {
return Err(std::io::Error::last_os_error());
}
Ok(unsafe { std::mem::transmute_copy::<usize, F>(&fn_ptr) })
}
fn get_accept_ex(socket: SOCKET) -> std::io::Result<AcceptExFn> {
get_extension_fn(socket, &WSAID_ACCEPTEX)
}
fn get_connect_ex(socket: SOCKET) -> std::io::Result<ConnectExFn> {
get_extension_fn(socket, &WSAID_CONNECTEX)
}
struct WorkerState {
iocp: HANDLE,
queues: Box<[SpscQueue<IoRequest>]>,
slots: Box<[WakerSlot]>,
free_slots: TreiberStack,
wait_slots: Box<[WaitSlot]>,
free_wait_slots: TreiberStack,
waiting_queue: TreiberStack,
is_sleeping: AtomicBool,
cancel_queue: TreiberStack,
}
unsafe impl Send for WorkerState {}
unsafe impl Sync for WorkerState {}
static WORKERS: OnceLock<Box<[WorkerState]>> = OnceLock::new();
static SHUTDOWN: AtomicBool = AtomicBool::new(false);
pub fn init_runtime(
workers: usize,
ring_depth: u32,
_buffer_pool_size: usize,
_chunk_size: usize,
_pin_cpus: &[usize],
) {
let config = GlobalConfig { workers };
if GLOBAL_CONFIG.set(config).is_err() {
return;
}
let mut worker_states = Vec::with_capacity(workers);
for _ in 0..workers {
let mut queues = Vec::with_capacity(512);
for _ in 0..512 {
queues.push(SpscQueue::new(256));
}
let queues = queues.into_boxed_slice();
let mut slots = Vec::with_capacity(ring_depth as usize);
for _ in 0..ring_depth {
slots.push(WakerSlot {
waker_data: AtomicPtr::new(std::ptr::null_mut()),
waker_vtable: AtomicPtr::new(std::ptr::null_mut()),
waker_lock: AtomicBool::new(false),
result: AtomicI32::new(0),
completed: AtomicBool::new(false),
dropped: AtomicBool::new(false),
origin_socket: AtomicUsize::new(usize::MAX),
});
}
let slots = slots.into_boxed_slice();
let free_slots = TreiberStack::new(ring_depth as usize);
for i in 0..ring_depth {
free_slots.push(i);
}
let cancel_queue = TreiberStack::new(ring_depth as usize);
let wait_slots_depth = 65536;
let mut wait_slots = Vec::with_capacity(wait_slots_depth);
for _ in 0..wait_slots_depth {
wait_slots.push(WaitSlot {
waker_data: AtomicPtr::new(std::ptr::null_mut()),
waker_vtable: AtomicPtr::new(std::ptr::null_mut()),
});
}
let wait_slots = wait_slots.into_boxed_slice();
let free_wait_slots = TreiberStack::new(wait_slots_depth);
for i in 0..wait_slots_depth {
free_wait_slots.push(i as u32);
}
let waiting_queue = TreiberStack::new(wait_slots_depth);
let iocp =
unsafe { CreateIoCompletionPort(INVALID_HANDLE_VALUE, std::ptr::null_mut(), 0, 1) };
assert!(!iocp.is_null(), "Failed to create IOCP handle");
worker_states.push(WorkerState {
iocp,
queues,
slots,
free_slots,
wait_slots,
free_wait_slots,
waiting_queue,
is_sleeping: AtomicBool::new(false),
cancel_queue,
});
}
let worker_states = worker_states.into_boxed_slice();
let _ = WORKERS.set(worker_states);
for worker_idx in 0..workers {
std::thread::Builder::new()
.name(format!("dtact-io-worker-{worker_idx}"))
.spawn(move || {
let state = &WORKERS.get().unwrap()[worker_idx];
run_windows_worker_loop(state);
})
.expect("Failed to spawn dtact-io worker thread");
}
}
pub fn init(workers: usize) {
init_runtime(workers, 1024, 0, 0, &[]);
}
pub fn shutdown_runtime() {
SHUTDOWN.store(true, Ordering::Release);
if let Some(workers) = WORKERS.get() {
for state in workers {
unsafe {
PostQueuedCompletionStatus(state.iocp, 0, WAKE_KEY, std::ptr::null_mut());
}
}
}
}
fn run_windows_worker_loop(state: &WorkerState) {
let iocp = state.iocp;
let mut entries: [OVERLAPPED_ENTRY; 64] = unsafe { std::mem::zeroed() };
loop {
if SHUTDOWN.load(Ordering::Relaxed) {
break;
}
let mut pushed = false;
for q in &state.queues {
while let Some(req) = q.pop() {
pushed = true;
submit_windows_request(state, req);
}
}
while let Some(slot_idx) = state.cancel_queue.pop() {
pushed = true;
cancel_windows_slot(state, slot_idx as usize);
}
let any_pending = state.queues.iter().any(|q| !q.is_empty());
let timeout_ms = if pushed || any_pending { 0 } else { u32::MAX };
state.is_sleeping.store(true, Ordering::SeqCst);
fence(Ordering::SeqCst);
let missed = state.queues.iter().any(|q| !q.is_empty());
let effective_timeout = if missed { 0 } else { timeout_ms };
let mut removed: u32 = 0;
let ok = unsafe {
GetQueuedCompletionStatusEx(
iocp,
entries.as_mut_ptr(),
entries.len() as u32,
&raw mut removed,
effective_timeout,
0,
)
};
state.is_sleeping.store(false, Ordering::Release);
if ok == 0 {
continue;
}
for entry in entries.iter().take(removed as usize) {
if entry.lpCompletionKey == WAKE_KEY {
continue;
}
let ov_ptr = entry.lpOverlapped.cast::<IoOverlapped>();
if ov_ptr.is_null() {
continue;
}
process_windows_completion(state, ov_ptr);
}
}
}
fn submit_read(state: &WorkerState, socket: usize, buf_ptr: *mut u8, len: usize, slot_idx: usize) {
state.slots[slot_idx]
.origin_socket
.store(socket, Ordering::Relaxed);
let ov = Box::new(IoOverlapped {
overlapped: unsafe { std::mem::zeroed() },
slot_idx,
issuing_socket: socket,
accept_socket: 0,
accept_addr_buf: [0u8; 288],
});
let ov_ptr = Box::into_raw(ov);
let wsabuf = WSABUF {
len: len as u32,
buf: buf_ptr,
};
let mut flags: u32 = 0;
let res = unsafe {
WSARecv(
socket,
&raw const wsabuf,
1,
std::ptr::null_mut(),
&raw mut flags,
ov_ptr.cast::<OVERLAPPED>(),
None,
)
};
handle_immediate_or_pending(state, slot_idx, ov_ptr, res);
}
fn submit_write(
state: &WorkerState,
socket: usize,
buf_ptr: *const u8,
len: usize,
slot_idx: usize,
) {
state.slots[slot_idx]
.origin_socket
.store(socket, Ordering::Relaxed);
let ov = Box::new(IoOverlapped {
overlapped: unsafe { std::mem::zeroed() },
slot_idx,
issuing_socket: socket,
accept_socket: 0,
accept_addr_buf: [0u8; 288],
});
let ov_ptr = Box::into_raw(ov);
let wsabuf = WSABUF {
len: len as u32,
buf: buf_ptr.cast_mut(),
};
let res = unsafe {
WSASend(
socket,
&raw const wsabuf,
1,
std::ptr::null_mut(),
0,
ov_ptr.cast::<OVERLAPPED>(),
None,
)
};
handle_immediate_or_pending(state, slot_idx, ov_ptr, res);
}
fn submit_accept(state: &WorkerState, listen_socket: usize, accept_socket: usize, slot_idx: usize) {
state.slots[slot_idx]
.origin_socket
.store(listen_socket, Ordering::Relaxed);
let accept_fn = match get_accept_ex(listen_socket) {
Ok(f) => f,
Err(e) => {
complete_with_error(state, slot_idx, &e);
return;
}
};
let mut ov = Box::new(IoOverlapped {
overlapped: unsafe { std::mem::zeroed() },
slot_idx,
issuing_socket: listen_socket,
accept_socket,
accept_addr_buf: [0u8; 288],
});
let buf_ptr = ov.accept_addr_buf.as_mut_ptr();
let ov_ptr = Box::into_raw(ov);
let mut bytes_received: u32 = 0;
let res = unsafe {
accept_fn(
listen_socket,
accept_socket,
buf_ptr.cast::<core::ffi::c_void>(),
0,
144,
144,
&raw mut bytes_received,
ov_ptr.cast::<OVERLAPPED>(),
)
};
let res = if res != 0 { 0 } else { -1 };
handle_immediate_or_pending(state, slot_idx, ov_ptr, res);
}
fn submit_connect(
state: &WorkerState,
socket: usize,
addr: SOCKADDR_STORAGE,
addr_len: i32,
slot_idx: usize,
) {
state.slots[slot_idx]
.origin_socket
.store(socket, Ordering::Relaxed);
let connect_fn = match get_connect_ex(socket) {
Ok(f) => f,
Err(e) => {
complete_with_error(state, slot_idx, &e);
return;
}
};
let ov = Box::new(IoOverlapped {
overlapped: unsafe { std::mem::zeroed() },
slot_idx,
issuing_socket: socket,
accept_socket: 0,
accept_addr_buf: [0u8; 288],
});
let ov_ptr = Box::into_raw(ov);
let mut bytes_sent: u32 = 0;
let res = unsafe {
connect_fn(
socket,
(&raw const addr).cast::<SOCKADDR>(),
addr_len,
std::ptr::null_mut(),
0,
&raw mut bytes_sent,
ov_ptr.cast::<OVERLAPPED>(),
)
};
let res = if res != 0 { 0 } else { -1 };
handle_immediate_or_pending(state, slot_idx, ov_ptr, res);
}
#[allow(clippy::needless_pass_by_value)]
fn submit_windows_request(state: &WorkerState, req: IoRequest) {
match req {
IoRequest::Cancel { slot_idx } => {
let _ = slot_idx;
}
IoRequest::Read {
socket,
buf_ptr,
len,
slot_idx,
} => submit_read(state, socket, buf_ptr, len, slot_idx),
IoRequest::Write {
socket,
buf_ptr,
len,
slot_idx,
} => submit_write(state, socket, buf_ptr, len, slot_idx),
IoRequest::Accept {
listen_socket,
accept_socket,
slot_idx,
} => submit_accept(state, listen_socket, accept_socket, slot_idx),
IoRequest::Connect {
socket,
addr,
addr_len,
slot_idx,
} => submit_connect(state, socket, addr, addr_len, slot_idx),
IoRequest::SendTo {
socket,
buf_ptr,
len,
addr,
addr_len,
slot_idx,
} => submit_send_to(state, socket, buf_ptr, len, addr, addr_len, slot_idx),
IoRequest::RecvFrom {
socket,
buf_ptr,
len,
from_ptr,
from_len_ptr,
slot_idx,
} => submit_recv_from(
state,
socket,
buf_ptr,
len,
from_ptr,
from_len_ptr,
slot_idx,
),
}
}
#[allow(clippy::too_many_arguments)]
fn submit_send_to(
state: &WorkerState,
socket: usize,
buf_ptr: *const u8,
len: usize,
addr: SOCKADDR_STORAGE,
addr_len: i32,
slot_idx: usize,
) {
state.slots[slot_idx]
.origin_socket
.store(socket, Ordering::Relaxed);
let ov = Box::new(IoOverlapped {
overlapped: unsafe { std::mem::zeroed() },
slot_idx,
issuing_socket: socket,
accept_socket: 0,
accept_addr_buf: [0u8; 288],
});
let ov_ptr = Box::into_raw(ov);
let wsabuf = WSABUF {
len: len as u32,
buf: buf_ptr.cast_mut(),
};
let res = unsafe {
WSASendTo(
socket,
&raw const wsabuf,
1,
std::ptr::null_mut(),
0,
std::ptr::addr_of!(addr).cast::<SOCKADDR>(),
addr_len,
ov_ptr.cast::<OVERLAPPED>(),
None,
)
};
handle_immediate_or_pending(state, slot_idx, ov_ptr, res);
}
#[allow(clippy::too_many_arguments)]
fn submit_recv_from(
state: &WorkerState,
socket: usize,
buf_ptr: *mut u8,
len: usize,
from_ptr: *mut SOCKADDR_STORAGE,
from_len_ptr: *mut i32,
slot_idx: usize,
) {
state.slots[slot_idx]
.origin_socket
.store(socket, Ordering::Relaxed);
let ov = Box::new(IoOverlapped {
overlapped: unsafe { std::mem::zeroed() },
slot_idx,
issuing_socket: socket,
accept_socket: 0,
accept_addr_buf: [0u8; 288],
});
let ov_ptr = Box::into_raw(ov);
let wsabuf = WSABUF {
len: len as u32,
buf: buf_ptr,
};
let mut flags: u32 = 0;
let res = unsafe {
WSARecvFrom(
socket,
&raw const wsabuf,
1,
std::ptr::null_mut(),
&raw mut flags,
from_ptr.cast::<SOCKADDR>(),
from_len_ptr,
ov_ptr.cast::<OVERLAPPED>(),
None,
)
};
handle_immediate_or_pending(state, slot_idx, ov_ptr, res);
}
fn handle_immediate_or_pending(
state: &WorkerState,
slot_idx: usize,
ov_ptr: *mut IoOverlapped,
res: i32,
) {
if res == 0 {
return;
}
let err = unsafe { WSAGetLastError() };
if err == WSA_IO_PENDING {
return;
}
unsafe {
drop(Box::from_raw(ov_ptr));
}
complete_with_error(state, slot_idx, &std::io::Error::from_raw_os_error(err));
}
fn complete_with_error(state: &WorkerState, slot_idx: usize, err: &std::io::Error) {
let res = -err.raw_os_error().unwrap_or(WSAEINVAL);
finish_slot(state, slot_idx, res);
}
fn process_windows_completion(state: &WorkerState, ov_ptr: *mut IoOverlapped) {
let ov = unsafe { Box::from_raw(ov_ptr) };
let slot_idx = ov.slot_idx;
let socket = ov.issuing_socket as SOCKET;
let mut transferred: u32 = 0;
let mut flags: u32 = 0;
let ok = unsafe {
WSAGetOverlappedResult(
socket,
(&raw const ov.overlapped).cast_mut(),
&raw mut transferred,
0,
&raw mut flags,
)
};
let res: i32 = if ok == 0 {
let err = unsafe { WSAGetLastError() };
-err
} else if ov.accept_socket != 0 {
ov.accept_socket as i32
} else {
transferred as i32
};
finish_slot(state, slot_idx, res);
}
fn finish_slot(state: &WorkerState, slot_idx: usize, res: i32) {
let slot = &state.slots[slot_idx];
io_trace!(
"[dtact-io] t={} slot={} res={} B_kernel_complete",
trace_now_us(),
slot_idx,
res
);
slot.result.store(res, Ordering::Release);
slot.lock_waker();
let data = slot
.waker_data
.swap(std::ptr::null_mut(), Ordering::Relaxed);
let vtable = slot
.waker_vtable
.swap(std::ptr::null_mut(), Ordering::Relaxed);
slot.unlock_waker();
slot.completed.store(true, Ordering::Release);
if slot.dropped.load(Ordering::Acquire) {
state.free_slots.push(slot_idx as u32);
wake_next_waiting_fiber(state);
} else if !data.is_null() && !vtable.is_null() {
let raw = RawWaker::new(data.cast_const(), unsafe { &*vtable });
let w = unsafe { Waker::from_raw(raw) };
w.wake();
}
}
const fn cancel_windows_slot(state: &WorkerState, slot_idx: usize) {
let _ = (state, slot_idx);
}
pub struct DtactIoFuture {
pub worker_idx: usize,
pub fd: u32,
pub direct_fd_idx: u32,
pub op: OpCode,
pub buf_ptr: *mut u8,
pub len: usize,
pub offset: i64,
pub addr: Option<SOCKADDR_STORAGE>,
pub addr_len: i32,
pub slot_idx: Option<usize>,
accept_socket: std::cell::Cell<usize>,
from_ptr: *mut SOCKADDR_STORAGE,
from_len_ptr: *mut i32,
}
unsafe impl Send for DtactIoFuture {}
unsafe impl Sync for DtactIoFuture {}
impl DtactIoFuture {
#[allow(clippy::too_many_arguments)]
pub const fn new(
worker_idx: usize,
fd: u32,
direct_fd_idx: u32,
op: OpCode,
buf_ptr: *mut u8,
len: usize,
offset: i64,
addr: Option<SOCKADDR_STORAGE>,
addr_len: i32,
slot_idx: Option<usize>,
) -> Self {
Self {
worker_idx,
fd,
direct_fd_idx,
op,
buf_ptr,
len,
offset,
addr,
addr_len,
slot_idx,
accept_socket: std::cell::Cell::new(0),
from_ptr: std::ptr::null_mut(),
from_len_ptr: std::ptr::null_mut(),
}
}
fn create_io_request(&self, slot_idx: usize) -> IoRequest {
match self.op {
OpCode::Read => IoRequest::Read {
socket: self.fd as usize,
buf_ptr: self.buf_ptr,
len: self.len,
slot_idx,
},
OpCode::Write => IoRequest::Write {
socket: self.fd as usize,
buf_ptr: self.buf_ptr,
len: self.len,
slot_idx,
},
OpCode::Accept => {
if self.accept_socket.get() == 0 {
let s = unsafe {
WSASocketW(
i32::from(AF_INET),
SOCK_STREAM,
0,
std::ptr::null(),
0,
WSA_FLAG_OVERLAPPED,
)
};
self.accept_socket.set(s as usize);
}
IoRequest::Accept {
listen_socket: self.fd as usize,
accept_socket: self.accept_socket.get(),
slot_idx,
}
}
OpCode::Connect => IoRequest::Connect {
socket: self.fd as usize,
addr: self.addr.unwrap(),
addr_len: self.addr_len,
slot_idx,
},
OpCode::SendTo => IoRequest::SendTo {
socket: self.fd as usize,
buf_ptr: self.buf_ptr,
len: self.len,
addr: self.addr.unwrap(),
addr_len: self.addr_len,
slot_idx,
},
OpCode::RecvFrom => IoRequest::RecvFrom {
socket: self.fd as usize,
buf_ptr: self.buf_ptr,
len: self.len,
from_ptr: self.from_ptr,
from_len_ptr: self.from_len_ptr,
slot_idx,
},
}
}
}
impl DtactIoFuture {
fn acquire_op_slot(state: &WorkerState, cx: &Context<'_>) -> Option<usize> {
if let Some(i) = state.free_slots.pop() {
return Some(i as usize);
}
let wait_idx = state.free_wait_slots.pop()?;
let wait_slot = &state.wait_slots[wait_idx as usize];
wait_slot
.waker_data
.store(cx.waker().data().cast_mut(), Ordering::Relaxed);
wait_slot.waker_vtable.store(
std::ptr::from_ref::<RawWakerVTable>(cx.waker().vtable()).cast_mut(),
Ordering::Relaxed,
);
state.waiting_queue.push(wait_idx);
let idx = state.free_slots.pop()?;
wait_slot
.waker_data
.store(std::ptr::null_mut(), Ordering::Relaxed);
wait_slot
.waker_vtable
.store(std::ptr::null_mut(), Ordering::Relaxed);
Some(idx as usize)
}
fn submit(self: Pin<&mut Self>, cx: &Context<'_>) -> Option<usize> {
let this = self.get_mut();
let state = &WORKERS.get().unwrap()[this.worker_idx];
let Some(idx) = Self::acquire_op_slot(state, cx) else {
cx.waker().wake_by_ref();
return None;
};
let slot = &state.slots[idx];
slot.completed.store(false, Ordering::Relaxed);
slot.dropped.store(false, Ordering::Relaxed);
slot.lock_waker();
slot.waker_data
.store(cx.waker().data().cast_mut(), Ordering::Relaxed);
slot.waker_vtable.store(
std::ptr::from_ref::<RawWakerVTable>(cx.waker().vtable()).cast_mut(),
Ordering::Relaxed,
);
slot.unlock_waker();
let req = this.create_io_request(idx);
let q_idx = get_local_thread_id() % state.queues.len();
let queue = &state.queues[q_idx];
io_trace!(
"[dtact-io] t={} slot={} fd={} op={:?} A_submit",
trace_now_us(),
idx,
this.fd,
this.op
);
if queue.push(req).is_err() {
slot.lock_waker();
slot.waker_data
.store(std::ptr::null_mut(), Ordering::Relaxed);
slot.waker_vtable
.store(std::ptr::null_mut(), Ordering::Relaxed);
slot.unlock_waker();
state.free_slots.push(idx as u32);
wake_next_waiting_fiber(state);
cx.waker().wake_by_ref();
return None;
}
fence(Ordering::SeqCst);
if state.is_sleeping.load(Ordering::SeqCst) {
unsafe {
PostQueuedCompletionStatus(state.iocp, 0, WAKE_KEY, std::ptr::null_mut());
}
}
this.slot_idx = Some(idx);
Some(idx)
}
}
impl Future for DtactIoFuture {
type Output = std::io::Result<usize>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let slot_idx = if let Some(idx) = self.slot_idx {
idx
} else {
let Some(idx) = self.as_mut().submit(cx) else {
return Poll::Pending;
};
idx
};
let state = &WORKERS.get().unwrap()[self.worker_idx];
let slot = &state.slots[slot_idx];
if slot.completed.load(Ordering::Acquire) {
let res = slot.result.load(Ordering::Acquire);
io_trace!(
"[dtact-io] t={} slot={} res={} C_fiber_resumed",
trace_now_us(),
slot_idx,
res
);
slot.lock_waker();
slot.waker_data
.store(std::ptr::null_mut(), Ordering::Relaxed);
slot.waker_vtable
.store(std::ptr::null_mut(), Ordering::Relaxed);
slot.unlock_waker();
state.free_slots.push(slot_idx as u32);
self.slot_idx = None;
wake_next_waiting_fiber(state);
if res < 0 {
Poll::Ready(Err(std::io::Error::from_raw_os_error(-res)))
} else {
Poll::Ready(Ok(res as usize))
}
} else {
let new_data = cx.waker().data().cast_mut();
let new_vtable = std::ptr::from_ref::<RawWakerVTable>(cx.waker().vtable()).cast_mut();
slot.lock_waker();
let old_data = slot.waker_data.load(Ordering::Relaxed);
let old_vtable = slot.waker_vtable.load(Ordering::Relaxed);
if old_data != new_data || old_vtable != new_vtable {
slot.waker_data.store(new_data, Ordering::Relaxed);
slot.waker_vtable.store(new_vtable, Ordering::Relaxed);
}
slot.unlock_waker();
Poll::Pending
}
}
}
impl Drop for DtactIoFuture {
fn drop(&mut self) {
let Some(idx) = self.slot_idx else { return };
let Some(state) = WORKERS.get().and_then(|w| w.get(self.worker_idx)) else {
return;
};
let slot = &state.slots[idx];
slot.lock_waker();
slot.waker_data
.store(std::ptr::null_mut(), Ordering::Relaxed);
slot.waker_vtable
.store(std::ptr::null_mut(), Ordering::Relaxed);
slot.unlock_waker();
if slot.completed.load(Ordering::Acquire) {
state.free_slots.push(idx as u32);
wake_next_waiting_fiber(state);
return;
}
slot.dropped.store(true, Ordering::Release);
let q_idx = get_local_thread_id() % state.queues.len();
let _ = state.queues[q_idx].push(IoRequest::Cancel { slot_idx: idx });
state.cancel_queue.push(idx as u32);
fence(Ordering::SeqCst);
if state.is_sleeping.load(Ordering::SeqCst) {
unsafe {
PostQueuedCompletionStatus(state.iocp, 0, WAKE_KEY, std::ptr::null_mut());
}
}
}
}
pub struct DtactTcpStream {
inner: std::net::TcpStream,
worker_idx: usize,
}
fn pick_worker(socket: usize) -> usize {
let num_workers = GLOBAL_CONFIG.get().map_or(1, |c| c.workers);
socket % num_workers
}
impl DtactTcpStream {
pub fn from_std(stream: std::net::TcpStream) -> std::io::Result<Self> {
stream.set_nonblocking(true)?;
stream.set_nodelay(true)?;
let socket = stream.as_raw_socket() as usize;
let worker_idx = pick_worker(socket);
let state = &WORKERS.get().unwrap()[worker_idx];
let res = unsafe { CreateIoCompletionPort(socket as HANDLE, state.iocp, socket, 0) };
if res.is_null() {
return Err(std::io::Error::last_os_error());
}
Ok(Self {
inner: stream,
worker_idx,
})
}
pub async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
if buf.is_empty() {
return Ok(0);
}
DtactIoFuture::new(
self.worker_idx,
self.inner.as_raw_socket() as u32,
u32::MAX,
OpCode::Read,
buf.as_mut_ptr(),
buf.len(),
0,
None,
0,
None,
)
.await
}
pub async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
if buf.is_empty() {
return Ok(0);
}
DtactIoFuture::new(
self.worker_idx,
self.inner.as_raw_socket() as u32,
u32::MAX,
OpCode::Write,
buf.as_ptr().cast_mut(),
buf.len(),
0,
None,
0,
None,
)
.await
}
pub async fn connect(addr: SocketAddr) -> std::io::Result<Self> {
let domain = match addr {
SocketAddr::V4(_) => AF_INET,
SocketAddr::V6(_) => AF_INET6,
};
let raw_socket = unsafe {
WSASocketW(
i32::from(domain),
SOCK_STREAM,
0,
std::ptr::null(),
0,
WSA_FLAG_OVERLAPPED,
)
};
if raw_socket == INVALID_SOCKET {
return Err(std::io::Error::last_os_error());
}
let any_addr = match addr {
SocketAddr::V4(_) => socket_addr_to_win(&"0.0.0.0:0".parse().unwrap()),
SocketAddr::V6(_) => socket_addr_to_win(&"[::]:0".parse().unwrap()),
};
let (bind_addr, bind_len) = any_addr;
let bind_res = unsafe {
bind(
raw_socket,
(&raw const bind_addr).cast::<SOCKADDR>(),
bind_len,
)
};
if bind_res != 0 {
let e = std::io::Error::last_os_error();
unsafe { closesocket(raw_socket) };
return Err(e);
}
let stream = unsafe { std::net::TcpStream::from_raw_socket(raw_socket as RawSocket) };
stream.set_nonblocking(true)?;
stream.set_nodelay(true)?;
let worker_idx = pick_worker(raw_socket as usize);
let state = &WORKERS.get().unwrap()[worker_idx];
let assoc = unsafe {
CreateIoCompletionPort(raw_socket as HANDLE, state.iocp, raw_socket as usize, 0)
};
if assoc.is_null() {
return Err(std::io::Error::last_os_error());
}
let (win_addr, win_len) = socket_addr_to_win(&addr);
let res = DtactIoFuture::new(
worker_idx,
raw_socket as u32,
u32::MAX,
OpCode::Connect,
std::ptr::null_mut(),
0,
0,
Some(win_addr),
win_len,
None,
)
.await;
match res {
Ok(_) => {
unsafe {
setsockopt(
raw_socket,
SOL_SOCKET,
SO_UPDATE_CONNECT_CONTEXT,
std::ptr::null(),
0,
);
}
Ok(Self {
inner: stream,
worker_idx,
})
}
Err(e) => Err(e),
}
}
}
impl crate::io::AsyncRead for DtactTcpStream {
async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
self.read(buf).await
}
}
impl crate::io::AsyncWrite for DtactTcpStream {
async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
self.write(buf).await
}
}
pub struct DtactTcpListener {
inner: std::net::TcpListener,
worker_idx: usize,
}
impl DtactTcpListener {
pub fn from_std(listener: std::net::TcpListener) -> std::io::Result<Self> {
listener.set_nonblocking(true)?;
let socket = listener.as_raw_socket() as usize;
let worker_idx = pick_worker(socket);
let state = &WORKERS.get().unwrap()[worker_idx];
let res = unsafe { CreateIoCompletionPort(socket as HANDLE, state.iocp, socket, 0) };
if res.is_null() {
return Err(std::io::Error::last_os_error());
}
Ok(Self {
inner: listener,
worker_idx,
})
}
pub async fn accept(&self) -> std::io::Result<(DtactTcpStream, SocketAddr)> {
let listen_socket = self.inner.as_raw_socket() as usize;
let fut = DtactIoFuture::new(
self.worker_idx,
listen_socket as u32,
u32::MAX,
OpCode::Accept,
std::ptr::null_mut(),
0,
0,
None,
0,
None,
);
let res = fut.await?;
let accept_socket = res as usize;
unsafe {
setsockopt(
accept_socket,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
(&raw const listen_socket).cast::<u8>(),
std::mem::size_of::<usize>() as i32,
);
}
let stream = unsafe { std::net::TcpStream::from_raw_socket(accept_socket as RawSocket) };
stream.set_nonblocking(true)?;
let peer_addr = stream.peer_addr()?;
let client = DtactTcpStream::from_std(stream)?;
Ok((client, peer_addr))
}
}
pub struct DtactUdpSocket {
inner: std::net::UdpSocket,
worker_idx: usize,
}
impl DtactUdpSocket {
pub fn bind(addr: SocketAddr) -> impl Future<Output = std::io::Result<Self>> {
std::future::ready(std::net::UdpSocket::bind(addr).and_then(Self::from_std))
}
pub fn from_std(socket: std::net::UdpSocket) -> std::io::Result<Self> {
socket.set_nonblocking(true)?;
let raw = socket.as_raw_socket() as usize;
let worker_idx = pick_worker(raw);
let state = &WORKERS.get().unwrap()[worker_idx];
let res = unsafe { CreateIoCompletionPort(raw as HANDLE, state.iocp, raw, 0) };
if res.is_null() {
return Err(std::io::Error::last_os_error());
}
Ok(Self {
inner: socket,
worker_idx,
})
}
pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
self.inner.local_addr()
}
pub async fn send_to(&self, buf: &[u8], target: SocketAddr) -> std::io::Result<usize> {
let (win_addr, win_len) = socket_addr_to_win(&target);
DtactIoFuture::new(
self.worker_idx,
self.inner.as_raw_socket() as u32,
u32::MAX,
OpCode::SendTo,
buf.as_ptr().cast_mut(),
buf.len(),
0,
Some(win_addr),
win_len,
None,
)
.await
}
pub async fn recv_from(&self, buf: &mut [u8]) -> std::io::Result<(usize, SocketAddr)> {
let mut from: SOCKADDR_STORAGE = unsafe { std::mem::zeroed() };
let mut from_len: i32 = std::mem::size_of::<SOCKADDR_STORAGE>() as i32;
let mut fut = DtactIoFuture::new(
self.worker_idx,
self.inner.as_raw_socket() as u32,
u32::MAX,
OpCode::RecvFrom,
buf.as_mut_ptr(),
buf.len(),
0,
None,
0,
None,
);
fut.from_ptr = &raw mut from;
fut.from_len_ptr = &raw mut from_len;
let n = fut.await?;
Ok((n, win_to_socket_addr(&from)))
}
pub fn connect(&self, addr: SocketAddr) -> impl Future<Output = std::io::Result<()>> {
std::future::ready(self.inner.connect(addr))
}
pub async fn send(&self, buf: &[u8]) -> std::io::Result<usize> {
DtactIoFuture::new(
self.worker_idx,
self.inner.as_raw_socket() as u32,
u32::MAX,
OpCode::Write,
buf.as_ptr().cast_mut(),
buf.len(),
0,
None,
0,
None,
)
.await
}
pub async fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
DtactIoFuture::new(
self.worker_idx,
self.inner.as_raw_socket() as u32,
u32::MAX,
OpCode::Read,
buf.as_mut_ptr(),
buf.len(),
0,
None,
0,
None,
)
.await
}
}
fn win_to_socket_addr(storage: &SOCKADDR_STORAGE) -> SocketAddr {
if storage.ss_family == AF_INET {
let sin = unsafe { &*(std::ptr::from_ref(storage).cast::<SOCKADDR_IN>()) };
let octets = unsafe { sin.sin_addr.S_un.S_addr }.to_ne_bytes();
let ip = std::net::Ipv4Addr::from(octets);
let port = u16::from_be(sin.sin_port);
SocketAddr::V4(std::net::SocketAddrV4::new(ip, port))
} else {
let sin6 = unsafe { &*(std::ptr::from_ref(storage).cast::<SOCKADDR_IN6>()) };
let ip = std::net::Ipv6Addr::from(unsafe { sin6.sin6_addr.u.Byte });
let port = u16::from_be(sin6.sin6_port);
let scope = unsafe { sin6.Anonymous.sin6_scope_id };
SocketAddr::V6(std::net::SocketAddrV6::new(
ip,
port,
sin6.sin6_flowinfo,
scope,
))
}
}
const fn socket_addr_to_win(addr: &SocketAddr) -> (SOCKADDR_STORAGE, i32) {
let mut storage: SOCKADDR_STORAGE = unsafe { std::mem::zeroed() };
let len = match addr {
SocketAddr::V4(a) => {
let sin = SOCKADDR_IN {
sin_family: AF_INET,
sin_port: a.port().to_be(),
sin_addr: IN_ADDR {
S_un: IN_ADDR_0 {
S_addr: u32::from_ne_bytes(a.ip().octets()),
},
},
sin_zero: [0; 8],
};
unsafe {
std::ptr::copy_nonoverlapping(
(&raw const sin).cast::<u8>(),
(&raw mut storage).cast::<u8>(),
std::mem::size_of_val(&sin),
);
}
std::mem::size_of_val(&sin) as i32
}
SocketAddr::V6(a) => {
let sin6 = SOCKADDR_IN6 {
sin6_family: AF_INET6,
sin6_port: a.port().to_be(),
sin6_flowinfo: a.flowinfo(),
sin6_addr: IN6_ADDR {
u: IN6_ADDR_0 {
Byte: a.ip().octets(),
},
},
Anonymous: SOCKADDR_IN6_0 {
sin6_scope_id: a.scope_id(),
},
};
unsafe {
std::ptr::copy_nonoverlapping(
(&raw const sin6).cast::<u8>(),
(&raw mut storage).cast::<u8>(),
std::mem::size_of_val(&sin6),
);
}
std::mem::size_of_val(&sin6) as i32
}
};
(storage, len)
}