From c0d4fdf7b40d3dcc7cce9430d091952ba071e0f8 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Thu, 21 May 2026 18:59:58 +0200 Subject: [PATCH] feat(client): allow TAP adapter sharing across threads The Windows client frame pump needs one task reading TAP frames while another writes relay frames back to the adapter. The TAP crate already owns the raw Windows file handle; this change makes the handle's thread-safety boundary explicit with `Send` and `Sync` impls and documents the Windows handle assumption next to the unsafe code. This does not add the pump yet. It only prepares the adapter type for the separate read/write loops that will wire TAP I/O to the relay session. Test Plan: - cargo fmt --check - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - CC_x86_64_pc_windows_msvc=clang-cl AR_x86_64_pc_windows_msvc=llvm-lib \ CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=lld-link cargo clippy \ -p lanparty-client-tap --target x86_64-pc-windows-msvc -- -D warnings - git diff --check Refs: PLAN.md --- crates/lanparty-client-tap/src/windows.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/lanparty-client-tap/src/windows.rs b/crates/lanparty-client-tap/src/windows.rs index cf1952c..8569cf7 100644 --- a/crates/lanparty-client-tap/src/windows.rs +++ b/crates/lanparty-client-tap/src/windows.rs @@ -234,6 +234,16 @@ pub fn available_adapters() -> Result> { #[derive(Debug)] struct OwnedHandle(HANDLE); +// SAFETY: Windows file handles are process-wide kernel object references that may be used from +// multiple threads. `OwnedHandle` only closes the handle in `Drop`; callers must still uphold any +// higher-level synchronization required by the device protocol. +unsafe impl Send for OwnedHandle {} + +// SAFETY: Sharing references to `OwnedHandle` only exposes the raw handle to synchronous Windows +// APIs. The handle value itself is immutable, and Windows permits issuing I/O on a file handle from +// more than one thread. +unsafe impl Sync for OwnedHandle {} + impl OwnedHandle { fn new(handle: HANDLE) -> io::Result { if handle == INVALID_HANDLE_VALUE {