Windows eventloop draft

This commit is contained in:
Andrew Golovashevich 2026-03-01 17:37:58 +03:00
parent 170a81ea3c
commit b2dcd7819a
10 changed files with 242 additions and 11 deletions

View File

@ -1,4 +1,6 @@
pub trait Address: Sized { use std::hash::Hash;
pub trait Address: Sized + PartialEq + Hash {
fn parse(raw: &str) -> Result<Self, String>; fn parse(raw: &str) -> Result<Self, String>;
fn to_string(self) -> String; fn to_string(self) -> String;

View File

View File

@ -1,3 +1,4 @@
mod address; mod address;
mod context;
pub use address::Address; pub use address::Address;

View File

@ -3,7 +3,7 @@ use std::mem::uninitialized;
use std::pin::Pin; use std::pin::Pin;
use std::ptr; use std::ptr;
pub struct Allocator<T> { pub(super) struct Allocator<T> {
map: HashMap<usize, Pin<Box<T>>>, map: HashMap<usize, Pin<Box<T>>>,
} }
@ -24,4 +24,7 @@ impl<T> Allocator<T> {
.unwrap_or_else(|| unreachable!()) .unwrap_or_else(|| unreachable!())
.as_mut(); .as_mut();
} }
pub unsafe fn free(&mut self, p: Pin<&mut T>) {
}
} }

View File

@ -1,3 +1,7 @@
mod windows; pub mod r#abstract;
mod allocator; mod allocator;
pub mod r#abstract; mod windows;
mod servers_context;
pub type Address = windows::WindowsAddressKnown;
use allocator::Allocator;

View File

@ -0,0 +1,7 @@
use super::Address;
pub trait ServersContext {
fn iter_servers(&self) -> impl Iterator<Item=Address>;
fn report_ping(&mut self, addr: Address, ping_ms: u64);
}

View File

@ -1,19 +1,24 @@
use crate::io::r#abstract::Address; use crate::io::r#abstract::Address;
use crate::io::windows::errors::{format_windows_err_code_result, throw_from_windows_err_code}; use crate::io::windows::errors::{format_windows_err_code_result, throw_from_windows_err_code};
use std::ffi::OsString; use std::ffi::OsString;
use std::hash::{Hash, Hasher};
use std::mem::size_of; use std::mem::size_of;
use std::mem::zeroed; use std::mem::zeroed;
use std::os::windows::ffi::OsStringExt; use std::os::windows::ffi::OsStringExt;
use std::ptr::{copy_nonoverlapping, null, null_mut};
use std::ptr; use std::ptr;
use std::ptr::{copy_nonoverlapping, null, null_mut};
use windows::Win32::Networking::WinSock::{
ADDRINFOW, AF_INET, AF_INET6, AF_UNSPEC, FreeAddrInfoW, GetAddrInfoW, SOCKADDR_IN,
SOCKADDR_IN6, SOCKADDR_STORAGE, WSAAddressToStringW, WSAGetLastError,
};
use windows::core::{PCWSTR, PWSTR}; use windows::core::{PCWSTR, PWSTR};
use windows::Win32::Networking::WinSock::{FreeAddrInfoW, GetAddrInfoW, WSAAddressToStringW, WSAGetLastError, ADDRINFOW, AF_INET, AF_INET6, SOCKADDR_IN, SOCKADDR_IN6, SOCKADDR_STORAGE};
pub struct WindowsAddress { #[derive(Clone, Copy)]
native: SOCKADDR_STORAGE, pub struct WindowsAddressAny {
pub(super) native: SOCKADDR_STORAGE,
} }
impl Address for WindowsAddress { impl Address for WindowsAddressAny {
fn parse(raw: &str) -> Result<Self, String> { fn parse(raw: &str) -> Result<Self, String> {
let encoded = raw.encode_utf16().collect::<Vec<_>>(); let encoded = raw.encode_utf16().collect::<Vec<_>>();
unsafe { unsafe {
@ -58,7 +63,7 @@ impl Address for WindowsAddress {
} }
} }
impl PartialEq for WindowsAddress { impl PartialEq for WindowsAddressAny {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match self.native.ss_family { match self.native.ss_family {
AF_INET => { AF_INET => {
@ -75,8 +80,63 @@ impl PartialEq for WindowsAddress {
let self_6 = *ptr::from_ref(&self.native).cast::<SOCKADDR_IN6>(); let self_6 = *ptr::from_ref(&self.native).cast::<SOCKADDR_IN6>();
let other_6 = *ptr::from_ref(&other.native).cast::<SOCKADDR_IN6>(); let other_6 = *ptr::from_ref(&other.native).cast::<SOCKADDR_IN6>();
return self_6.sin6_addr.u.Word == other_6.sin6_addr.u.Word; return self_6.sin6_addr.u.Word == other_6.sin6_addr.u.Word;
} },
_ => return false, _ => return false,
} }
} }
} }
impl Hash for WindowsAddressAny {
fn hash<H: Hasher>(&self, state: &mut H) {
match self.native.ss_family {
AF_INET => {
state.write_u16(self.native.ss_family.0);
unsafe {
let self_4 = *ptr::from_ref(&self.native).cast::<SOCKADDR_IN>();
state.write_u32(self_4.sin_addr.S_un.S_addr);
}
}
AF_INET6 => {
state.write_u16(self.native.ss_family.0);
unsafe {
let self_6 = *ptr::from_ref(&self.native).cast::<SOCKADDR_IN6>();
state.write(&self_6.sin6_addr.u.Byte);
}
}
_ => state.write_u16(AF_UNSPEC.0),
}
}
}
#[derive(Clone, Copy)]
pub struct WindowsAddressKnown {
pub(super) any: WindowsAddressAny,
}
impl Address for WindowsAddressKnown {
fn parse(raw: &str) -> Result<Self, String> {
let any = WindowsAddressAny::parse(raw)?;
match any.native.ss_family {
AF_INET | AF_INET6 => return Result::Ok(Self { any }),
_ => return Result::Err("Address resolved to unsupported AF".to_owned()),
}
}
fn to_string(self) -> String {
return self.any.to_string();
}
}
impl PartialEq for WindowsAddressKnown {
fn eq(&self, other: &Self) -> bool {
return self.any == other.any;
}
}
impl Eq for WindowsAddressKnown {}
impl Hash for WindowsAddressKnown {
fn hash<H: Hasher>(&self, state: &mut H) {
self.any.hash(state)
}
}

104
src/io/windows/eventloop.rs Normal file
View File

@ -0,0 +1,104 @@
use crate::io::allocator::Allocator;
use crate::io::servers_context::ServersContext;
use crate::io::windows::task::IoTask;
use crate::io::windows::WindowsAddressKnown;
use std::pin::Pin;
use std::ptr::NonNull;
use windows::Win32::Networking::WinSock::{
WSARecvFrom, WSASendTo, WSASocketW, AF_UNSPEC, IPPROTO_ICMP, SOCKADDR,
SOCKADDR_STORAGE, SOCK_RAW, WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED,
};
use windows::Win32::System::IO::OVERLAPPED;
pub unsafe fn run_eventloop<Ctx: ServersContext>(ctx: *mut Ctx) {
let mut heap = Allocator::<IoTask<CallbackData<_>>>::new();
let socket = WSASocketW(
AF_UNSPEC.0 as i32,
SOCK_RAW.0,
IPPROTO_ICMP.0,
None,
0,
WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT,
)
.unwrap();
let mut recv_task = NonNull::from_mut(&mut heap).as_mut().alloc();
let mut in_addr_size = 0i32;
let mut in_flags = 0u32;
loop {
for mut address in (*ctx).iter_servers() {
let mut data = NonNull::from_mut(&mut heap).as_mut().alloc();
data.init(CallbackData {
ctx: NonNull::new_unchecked(ctx),
allocator: NonNull::from_mut(&mut heap),
address,
});
let overlapped = data.get_overlapped();
let address =
NonNull::from_ref(&data.get_extra().address.any.native).cast::<SOCKADDR>();
WSASendTo(
socket,
data.as_ref().get_buffer_descriptors(),
None,
0,
Some(address.as_ptr()),
size_of::<SOCKADDR_STORAGE>() as i32,
Some(overlapped),
Some(sending_done::<Ctx>),
);
let overlapped = recv_task.get_overlapped();
let address =
NonNull::from_mut(&mut recv_task.get_extra().address.any.native).cast::<SOCKADDR>();
WSARecvFrom(
socket,
data.as_ref().get_buffer_descriptors(),
None,
&mut in_flags,
Some(address.as_ptr()),
Some(&mut in_addr_size),
Some(overlapped),
Some(receiving_done::<Ctx>),
);
}
}
}
struct CallbackData<Ctx: ServersContext> {
ctx: NonNull<Ctx>,
allocator: NonNull<Allocator<IoTask<Self>>>,
address: WindowsAddressKnown,
}
unsafe extern "system" fn sending_done<Ctx: ServersContext>(
dwerror: u32,
cbtransferred: u32,
lpoverlapped: *mut OVERLAPPED,
dwflags: u32,
) {
let mut task = Pin::new_unchecked(
lpoverlapped
.cast::<IoTask<CallbackData<Ctx>>>()
.as_mut()
.unwrap(),
);
task.get_extra().allocator.as_mut().free(task);
}
unsafe extern "system" fn receiving_done<Ctx: ServersContext>(
dwerror: u32,
cbtransferred: u32,
lpoverlapped: *mut OVERLAPPED,
dwflags: u32,
) {
let mut task = Pin::new_unchecked(
lpoverlapped
.cast::<IoTask<CallbackData<Ctx>>>()
.as_mut()
.unwrap(),
);
task.get_extra().allocator.as_mut().free(task);
}

View File

@ -1,2 +1,6 @@
mod address; mod address;
mod errors; mod errors;
mod eventloop;
mod task;
pub use address::{WindowsAddressAny, WindowsAddressKnown};

46
src/io/windows/task.rs Normal file
View File

@ -0,0 +1,46 @@
use std::marker::PhantomPinned;
use std::mem::zeroed;
use std::pin::Pin;
use std::ptr::NonNull;
use windows::Win32::Networking::WinSock::WSABUF;
use windows::Win32::System::IO::OVERLAPPED;
use windows::core::PSTR;
const _BUFF_LEN: usize = 100;
#[repr(C)]
pub struct IoTask<E> {
sys: OVERLAPPED,
__pinned: PhantomPinned,
buff_d: [WSABUF; 1],
buff: [u8; _BUFF_LEN],
extra: E,
}
impl<E> IoTask<E> {
pub unsafe fn init(self: &mut Pin<&mut Self>, extra: E) {
let self_unwrapped = Pin::into_inner_unchecked(NonNull::from_mut(self).read());
self_unwrapped.sys = zeroed();
self_unwrapped.buff_d[0].len = self_unwrapped.buff.len() as u32;
self_unwrapped.buff_d[0].buf = PSTR(self_unwrapped.buff.as_mut_ptr());
self_unwrapped.extra = extra
}
pub const BUFFER_LEN: usize = _BUFF_LEN;
pub fn get_buffer<'s>(self: &'s mut Pin<&mut Self>) -> &'s mut [u8; _BUFF_LEN] {
return &mut unsafe { Pin::into_inner_unchecked(NonNull::from_mut(self).read()) }.buff;
}
pub fn get_buffer_descriptors<'s>(self: &'s Pin<&Self>) -> &'s [WSABUF] {
return &unsafe { Pin::into_inner_unchecked(NonNull::from_ref(self).read()) }.buff_d;
}
pub fn get_extra<'s>(self: &'s mut Pin<&mut Self>) -> &'s mut E {
return &mut unsafe { Pin::into_inner_unchecked(NonNull::from_mut(self).read()) }.extra;
}
pub fn get_overlapped(self: &mut Pin<&mut Self>) -> *mut OVERLAPPED {
return &mut unsafe { Pin::into_inner_unchecked(NonNull::from_mut(self).read()) }.sys;
}
}