From 4c368c02b5afe837eb4b624a5e8def5f84dab73f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dav=C3=AD=C3=B0=20Steinn=20Geirsson?= Date: Sun, 22 Mar 2026 15:10:28 +0000 Subject: [PATCH] feat: concurrent ISO pipelining via nusb update and &self handlers Update nusb to c1380673 which allows multiple IsoEndpoint instances per address, enabling concurrent URB submission from separate threads. Change UsbInterfaceHandler trait methods from &mut self to &self and replace Arc>> with Arc. This removes the serialization bottleneck where the handler mutex was held for the entire USB transfer duration, causing ISO audio to play at ~67% speed. Handlers needing interior mutability (HID, CDC) now use Mutex on individual fields. Passthrough handlers already used Arc> internally and need no changes. Co-Authored-By: Claude Opus 4.6 (1M context) --- Cargo.lock | 2 +- cli/Cargo.toml | 2 +- cli/src/host.rs | 8 ++++---- cli/src/test_hid.rs | 27 +++++++++++---------------- flake.nix | 2 +- lib/Cargo.toml | 2 +- lib/examples/cdc_acm_serial.rs | 17 +++++------------ lib/examples/hid_keyboard.rs | 29 ++++++++++------------------- lib/src/cdc.rs | 22 ++++++++++------------ lib/src/device.rs | 17 +++++++---------- lib/src/hid.rs | 24 ++++++++++++------------ lib/src/host.rs | 18 +++++++----------- lib/src/interface.rs | 17 +++++++++++------ lib/src/lib.rs | 25 ++++++++++--------------- lib/src/util.rs | 11 +++++------ 15 files changed, 96 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a0d794..c72cab5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,7 +470,7 @@ dependencies = [ [[package]] name = "nusb" version = "0.2.3" -source = "git+https://git.dsg.is/dsg/nusb.git?rev=997a76cd#997a76cd3c5c528ea08ad005d85e47bc9a7b91b5" +source = "git+https://git.dsg.is/dsg/nusb.git?rev=c1380673#c1380673db9c692b8468104d7f3e81144a1ee12a" dependencies = [ "core-foundation", "core-foundation-sys", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 6ab4e7b..953726c 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -16,4 +16,4 @@ tokio-vsock = "0.7" tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal", "time", "net"] } log = "0.4" env_logger = "0.11" -nusb = { git = "https://git.dsg.is/dsg/nusb.git", rev = "997a76cd" } +nusb = { git = "https://git.dsg.is/dsg/nusb.git", rev = "c1380673" } diff --git a/cli/src/host.rs b/cli/src/host.rs index 0d287aa..63a67b9 100644 --- a/cli/src/host.rs +++ b/cli/src/host.rs @@ -121,11 +121,11 @@ fn build_usb_device(dev: nusb::Device, dev_info: nusb::DeviceInfo) -> Result>> = Arc::new(Mutex::new( - Box::new(NusbUsbHostInterfaceHandler::new(Arc::new(Mutex::new( + let handler: Arc = Arc::new( + NusbUsbHostInterfaceHandler::new(Arc::new(Mutex::new( claimed.clone(), - )))), - )); + ))), + ); let alt_settings: Vec = claimed .descriptors() diff --git a/cli/src/test_hid.rs b/cli/src/test_hid.rs index 64b7281..7453974 100644 --- a/cli/src/test_hid.rs +++ b/cli/src/test_hid.rs @@ -1,5 +1,5 @@ use log::info; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use usbip_rs::{ @@ -13,7 +13,7 @@ use crate::transport; async fn do_test_hid_session( mut stream: S, device: UsbDevice, - handler: Arc>>, + handler: Arc, ) -> std::io::Result<()> { // Send device info (simplified handshake) let handshake = UsbIpResponse::op_rep_import_success(&device).to_bytes()?; @@ -28,16 +28,13 @@ async fn do_test_hid_session() { - match usbip_rs::hid::UsbHidKeyboardReport::from_ascii(b'1') { - Ok(report) => { - hid.pending_key_events.push_back(report); - info!("Simulated key event '1'"); - } - Err(e) => { - info!("Failed to create key report: {}", e); - } + match usbip_rs::hid::UsbHidKeyboardReport::from_ascii(b'1') { + Ok(report) => { + handler_clone.pending_key_events.lock().unwrap().push_back(report); + info!("Simulated key event '1'"); + } + Err(e) => { + info!("Failed to create key report: {}", e); } } } @@ -49,9 +46,7 @@ async fn do_test_hid_session std::io::Result<()> { // Create simulated HID keyboard - let handler = Arc::new(Mutex::new( - Box::new(UsbHidKeyboardHandler::new_keyboard()) as Box - )); + let handler = Arc::new(UsbHidKeyboardHandler::new_keyboard()); let device = UsbDevice::new(0)?.with_interface( ClassCode::HID as u8, @@ -64,7 +59,7 @@ pub async fn run(addr: transport::TransportAddr) -> std::io::Result<()> { max_packet_size: 0x08, // 8 bytes interval: 10, }], - handler.clone(), + handler.clone() as Arc, )?; info!( diff --git a/flake.nix b/flake.nix index 77fc402..c37e0b0 100644 --- a/flake.nix +++ b/flake.nix @@ -31,7 +31,7 @@ src = self; - cargoHash = "sha256-xEyWh+bP7F+a0KpH24hVliC1JQMRIW0wfmUB3rFNvyk="; + cargoHash = "sha256-ol61Q+2IYDZ4ZvJiLYJdfqaN23thuZ1QHDOSvNXHhvY="; inherit nativeBuildInputs buildInputs; diff --git a/lib/Cargo.toml b/lib/Cargo.toml index a4e5a7d..bd5cf2e 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -14,7 +14,7 @@ num-traits = "0.2.15" num-derive = "0.4.2" rusb = "0.9.3" serde = { version = "1.0", features = ["derive"], optional = true } -nusb = { git = "https://git.dsg.is/dsg/nusb.git", rev = "997a76cd" } +nusb = { git = "https://git.dsg.is/dsg/nusb.git", rev = "c1380673" } tokio-util = { version = "0.7", features = ["rt"] } [dev-dependencies] diff --git a/lib/examples/cdc_acm_serial.rs b/lib/examples/cdc_acm_serial.rs index 886acd0..f3cea52 100644 --- a/lib/examples/cdc_acm_serial.rs +++ b/lib/examples/cdc_acm_serial.rs @@ -1,13 +1,12 @@ use log::*; use std::net::*; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::time::Duration; #[tokio::main] async fn main() { env_logger::init(); - let handler = Arc::new(Mutex::new(Box::new(usbip_rs::cdc::UsbCdcAcmHandler::new()) - as Box)); + let handler = Arc::new(usbip_rs::cdc::UsbCdcAcmHandler::new()); let server = Arc::new(usbip_rs::UsbIpServer::new_simulated(vec![ usbip_rs::UsbDevice::new(0) .expect("create device") @@ -17,7 +16,7 @@ async fn main() { 0x00, Some("Test CDC ACM"), usbip_rs::cdc::UsbCdcAcmHandler::endpoints(), - handler.clone(), + handler.clone() as Arc, ) .expect("add interface"), ])); @@ -27,13 +26,7 @@ async fn main() { loop { // sleep 1s tokio::time::sleep(Duration::new(1, 0)).await; - let mut handler = handler.lock().unwrap(); - if let Some(acm) = handler - .as_any() - .downcast_mut::() - { - acm.tx_buffer.push(b'a'); - info!("Simulate a char input"); - } + handler.tx_buffer.lock().unwrap().push(b'a'); + info!("Simulate a char input"); } } diff --git a/lib/examples/hid_keyboard.rs b/lib/examples/hid_keyboard.rs index 29aa47c..a0611e3 100644 --- a/lib/examples/hid_keyboard.rs +++ b/lib/examples/hid_keyboard.rs @@ -1,15 +1,12 @@ use log::*; use std::net::*; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::time::Duration; #[tokio::main] async fn main() { env_logger::init(); - let handler = Arc::new(Mutex::new( - Box::new(usbip_rs::hid::UsbHidKeyboardHandler::new_keyboard()) - as Box, - )); + let handler = Arc::new(usbip_rs::hid::UsbHidKeyboardHandler::new_keyboard()); let server = Arc::new(usbip_rs::UsbIpServer::new_simulated(vec![ usbip_rs::UsbDevice::new(0) .expect("create device") @@ -24,7 +21,7 @@ async fn main() { max_packet_size: 0x08, // 8 bytes interval: 10, }], - handler.clone(), + handler.clone() as Arc, ) .expect("add interface"), ])); @@ -34,19 +31,13 @@ async fn main() { loop { // sleep 1s tokio::time::sleep(Duration::new(1, 0)).await; - let mut handler = handler.lock().unwrap(); - if let Some(hid) = handler - .as_any() - .downcast_mut::() - { - match usbip_rs::hid::UsbHidKeyboardReport::from_ascii(b'1') { - Ok(report) => { - hid.pending_key_events.push_back(report); - info!("Simulate a key event"); - } - Err(e) => { - error!("Failed to create key report: {}", e); - } + match usbip_rs::hid::UsbHidKeyboardReport::from_ascii(b'1') { + Ok(report) => { + handler.pending_key_events.lock().unwrap().push_back(report); + info!("Simulate a key event"); + } + Err(e) => { + error!("Failed to create key report: {}", e); } } } diff --git a/lib/src/cdc.rs b/lib/src/cdc.rs index cd35b4b..091ad17 100644 --- a/lib/src/cdc.rs +++ b/lib/src/cdc.rs @@ -2,9 +2,9 @@ use super::*; /// A handler of a CDC ACM(Abstract Control Model) -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct UsbCdcAcmHandler { - pub tx_buffer: Vec, + pub tx_buffer: Mutex>, } impl Default for UsbCdcAcmHandler { @@ -18,7 +18,7 @@ pub const CDC_ACM_SUBCLASS: u8 = 0x02; impl UsbCdcAcmHandler { pub fn new() -> Self { - Self { tx_buffer: vec![] } + Self { tx_buffer: Mutex::new(vec![]) } } pub fn endpoints() -> Vec { @@ -50,7 +50,7 @@ impl UsbCdcAcmHandler { impl UsbInterfaceHandler for UsbCdcAcmHandler { fn handle_urb( - &mut self, + &self, _interface: &UsbInterface, request: UrbRequest, ) -> Result { @@ -74,15 +74,13 @@ impl UsbInterfaceHandler for UsbCdcAcmHandler { return Ok(UrbResponse::default()); } else { // bulk in - // Handle max packet size - return data in chunks of max_packet_size let max_packet_size = ep.max_packet_size as usize; - let resp = if self.tx_buffer.len() > max_packet_size { - // Return only the first chunk (max_packet_size bytes) - self.tx_buffer.drain(..max_packet_size).collect::>() + let mut tx_buffer = self.tx_buffer.lock().unwrap(); + let resp = if tx_buffer.len() > max_packet_size { + tx_buffer.drain(..max_packet_size).collect::>() } else { - // Return all data if it fits in one packet - let resp = self.tx_buffer.clone(); - self.tx_buffer.clear(); + let resp = tx_buffer.clone(); + tx_buffer.clear(); resp }; return Ok(UrbResponse { data: resp, ..Default::default() }); @@ -106,7 +104,7 @@ impl UsbInterfaceHandler for UsbCdcAcmHandler { ] } - fn as_any(&mut self) -> &mut dyn Any { + fn as_any(&self) -> &dyn Any { self } } diff --git a/lib/src/device.rs b/lib/src/device.rs index 1169e3d..1af87d0 100644 --- a/lib/src/device.rs +++ b/lib/src/device.rs @@ -188,13 +188,13 @@ impl UsbDevice { interface_protocol: u8, name: Option<&str>, endpoints: Vec, - handler: Arc>>, + handler: Arc, ) -> std::io::Result { let string_interface = match name { Some(name) => self.new_string(name)?, None => 0, }; - let class_specific_descriptor = handler.lock().unwrap().get_class_specific_descriptor(); + let class_specific_descriptor = handler.get_class_specific_descriptor(); self.interface_states.push(InterfaceState::new(UsbInterface { interface_class, interface_subclass, @@ -621,8 +621,7 @@ impl UsbDevice { } }; let inner = state.inner.read().await; - let mut handler = inner.active.handler.lock().unwrap(); - handler.handle_urb(&inner.active, UrbRequest { + inner.active.handler.handle_urb(&inner.active, UrbRequest { ep, transfer_buffer_length, setup: setup_packet, @@ -682,7 +681,7 @@ impl UsbDevice { let mut inner = state.inner.write().await; if (alt as usize) < inner.alt_settings.len() { // Notify the handler so it can update the physical device - inner.active.handler.lock().unwrap().set_alt_setting(alt)?; + inner.active.handler.set_alt_setting(alt)?; inner.active = inner.alt_settings[alt as usize].clone(); inner.current_alt = alt; info!("SET_INTERFACE: intf={intf_index} alt={alt}"); @@ -718,8 +717,7 @@ impl UsbDevice { } }; let inner = state.inner.read().await; - let mut handler = inner.active.handler.lock().unwrap(); - handler.handle_urb(&inner.active, UrbRequest { + inner.active.handler.handle_urb(&inner.active, UrbRequest { ep, transfer_buffer_length, setup: setup_packet, @@ -755,8 +753,7 @@ impl UsbDevice { Some(idx) => { let state = &self.interface_states[idx]; let inner = state.inner.read().await; - let mut handler = inner.active.handler.lock().unwrap(); - handler.handle_urb( + inner.active.handler.handle_urb( &inner.active, request, ) @@ -789,7 +786,7 @@ pub trait UsbDeviceHandler: std::fmt::Debug { fn handle_urb(&mut self, request: UrbRequest) -> Result; /// Helper to downcast to actual struct - fn as_any(&mut self) -> &mut dyn Any; + fn as_any(&self) -> &dyn Any; } #[cfg(test)] diff --git a/lib/src/hid.rs b/lib/src/hid.rs index d1621a0..f73ca97 100644 --- a/lib/src/hid.rs +++ b/lib/src/hid.rs @@ -13,12 +13,11 @@ enum UsbHidKeyboardHandlerState { } /// A handler of a HID keyboard -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug)] pub struct UsbHidKeyboardHandler { pub report_descriptor: Vec, - pub pending_key_events: VecDeque, - state: UsbHidKeyboardHandlerState, + pub pending_key_events: Mutex>, + state: Mutex, } /// A report of a HID keyboard @@ -59,8 +58,8 @@ impl UsbHidKeyboardReport { impl UsbHidKeyboardHandler { pub fn new_keyboard() -> Self { Self { - pending_key_events: VecDeque::new(), - state: UsbHidKeyboardHandlerState::Idle, + pending_key_events: Mutex::new(VecDeque::new()), + state: Mutex::new(UsbHidKeyboardHandlerState::Idle), report_descriptor: vec![ 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x06, // Usage (Keyboard) @@ -95,7 +94,7 @@ impl UsbHidKeyboardHandler { impl UsbInterfaceHandler for UsbHidKeyboardHandler { fn handle_urb( - &mut self, + &self, _interface: &UsbInterface, request: UrbRequest, ) -> Result { @@ -134,20 +133,21 @@ impl UsbInterfaceHandler for UsbHidKeyboardHandler { // interrupt transfer if let Direction::In = ep.direction() { // interrupt in - match self.state { + let mut state = self.state.lock().unwrap(); + match *state { UsbHidKeyboardHandlerState::Idle => { - if let Some(report) = self.pending_key_events.pop_front() { + if let Some(report) = self.pending_key_events.lock().unwrap().pop_front() { let mut resp = vec![report.modifier, 0]; resp.extend_from_slice(&report.keys); info!("HID key down"); - self.state = UsbHidKeyboardHandlerState::KeyDown; + *state = UsbHidKeyboardHandlerState::KeyDown; return Ok(UrbResponse { data: resp, ..Default::default() }); } } UsbHidKeyboardHandlerState::KeyDown => { let resp = vec![0; 6]; info!("HID key up"); - self.state = UsbHidKeyboardHandlerState::Idle; + *state = UsbHidKeyboardHandlerState::Idle; return Ok(UrbResponse { data: resp, ..Default::default() }); } } @@ -170,7 +170,7 @@ impl UsbInterfaceHandler for UsbHidKeyboardHandler { ] } - fn as_any(&mut self) -> &mut dyn Any { + fn as_any(&self) -> &dyn Any { self } } diff --git a/lib/src/host.rs b/lib/src/host.rs index c386663..d25d427 100644 --- a/lib/src/host.rs +++ b/lib/src/host.rs @@ -31,7 +31,7 @@ impl RusbUsbHostInterfaceHandler { impl UsbInterfaceHandler for RusbUsbHostInterfaceHandler { fn handle_urb( - &mut self, + &self, _interface: &UsbInterface, request: UrbRequest, ) -> Result { @@ -123,7 +123,7 @@ impl UsbInterfaceHandler for RusbUsbHostInterfaceHandler { vec![] } - fn as_any(&mut self) -> &mut dyn Any { + fn as_any(&self) -> &dyn Any { self } } @@ -186,7 +186,7 @@ impl UsbDeviceHandler for RusbUsbHostDeviceHandler { Ok(UrbResponse::default()) } - fn as_any(&mut self) -> &mut dyn Any { + fn as_any(&self) -> &dyn Any { self } } @@ -212,7 +212,7 @@ impl NusbUsbHostInterfaceHandler { } impl UsbInterfaceHandler for NusbUsbHostInterfaceHandler { - fn set_alt_setting(&mut self, alt: u8) -> Result<()> { + fn set_alt_setting(&self, alt: u8) -> Result<()> { let handle = self.handle.lock().unwrap(); handle.set_alt_setting(alt).wait().map_err(|e| { std::io::Error::other(format!("Failed to set alt setting {alt}: {e}")) @@ -220,7 +220,7 @@ impl UsbInterfaceHandler for NusbUsbHostInterfaceHandler { } fn handle_urb( - &mut self, + &self, _interface: &UsbInterface, request: UrbRequest, ) -> Result { @@ -425,7 +425,6 @@ impl UsbInterfaceHandler for NusbUsbHostInterfaceHandler { let mut endpoint = handle .iso_endpoint::(ep.address, num_packets) .map_err(|e| std::io::Error::other(format!("Failed to open ISO IN endpoint: {e}")))?; - let buf = endpoint.allocate(total_len); drop(handle); endpoint.submit(buf, packet_size); @@ -468,8 +467,6 @@ impl UsbInterfaceHandler for NusbUsbHostInterfaceHandler { .map_err(|e| std::io::Error::other(format!("Failed to open ISO OUT endpoint: {e}")))?; let mut buf = endpoint.allocate(total_len); - // allocate() returns a zero-length buffer with capacity; - // fill it from request data, zero-padding if needed let copy_len = request.data.len().min(total_len); if copy_len > 0 { buf.extend_from_slice(&request.data[..copy_len]); @@ -488,7 +485,6 @@ impl UsbInterfaceHandler for NusbUsbHostInterfaceHandler { } let mut response_descriptors = Vec::new(); - for (i, pkt) in completion.packets.iter().enumerate() { response_descriptors.push(IsoPacketDescriptor { offset: request.iso_packet_descriptors.get(i).map(|d| d.offset).unwrap_or(0), @@ -514,7 +510,7 @@ impl UsbInterfaceHandler for NusbUsbHostInterfaceHandler { vec![] } - fn as_any(&mut self) -> &mut dyn Any { + fn as_any(&self) -> &dyn Any { self } } @@ -640,7 +636,7 @@ impl UsbDeviceHandler for NusbUsbHostDeviceHandler { Ok(UrbResponse::default()) } - fn as_any(&mut self) -> &mut dyn Any { + fn as_any(&self) -> &dyn Any { self } } diff --git a/lib/src/interface.rs b/lib/src/interface.rs index 43da1d8..a72dbab 100644 --- a/lib/src/interface.rs +++ b/lib/src/interface.rs @@ -13,7 +13,7 @@ pub struct UsbInterface { pub class_specific_descriptor: Vec, #[cfg_attr(feature = "serde", serde(skip))] - pub handler: Arc>>, + pub handler: Arc, } /// Inner mutable state for an interface, protected by RwLock. @@ -103,24 +103,29 @@ pub struct UrbResponse { pub error_count: u32, } -/// A handler of a custom usb interface -pub trait UsbInterfaceHandler: std::fmt::Debug { +/// A handler of a custom usb interface. +/// +/// Methods take `&self` (not `&mut self`) so that multiple URBs can be +/// processed concurrently on the same interface — critical for isochronous +/// pipelining. Implementations that need interior mutability should use +/// `Mutex`, `AtomicU8`, etc. +pub trait UsbInterfaceHandler: std::fmt::Debug + Send + Sync { /// Return the class specific descriptor which is inserted between interface descriptor and endpoint descriptor fn get_class_specific_descriptor(&self) -> Vec; /// Handle a URB(USB Request Block) targeting at this interface fn handle_urb( - &mut self, + &self, interface: &UsbInterface, request: UrbRequest, ) -> Result; /// Notify the handler that the alternate setting has changed. /// For host passthrough handlers, this updates the underlying USB interface. - fn set_alt_setting(&mut self, _alt: u8) -> Result<()> { + fn set_alt_setting(&self, _alt: u8) -> Result<()> { Ok(()) } /// Helper to downcast to actual struct - fn as_any(&mut self) -> &mut dyn Any; + fn as_any(&self) -> &dyn Any; } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index d2bf645..b33b7a2 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -93,10 +93,9 @@ impl UsbIpServer { }); } - let handler = Arc::new(Mutex::new(Box::new(NusbUsbHostInterfaceHandler::new( - Arc::new(Mutex::new(intf.clone())), - )) - as Box)); + let handler: Arc = Arc::new( + NusbUsbHostInterfaceHandler::new(Arc::new(Mutex::new(intf.clone()))), + ); interface_states.push(InterfaceState::new(UsbInterface { interface_class: alt_setting.class(), interface_subclass: alt_setting.subclass(), @@ -239,10 +238,9 @@ impl UsbIpServer { }); } - let handler = Arc::new(Mutex::new(Box::new(RusbUsbHostInterfaceHandler::new( - handle.clone(), - )) - as Box)); + let handler: Arc = Arc::new( + RusbUsbHostInterfaceHandler::new(handle.clone()), + ); interface_states.push(InterfaceState::new(UsbInterface { interface_class: intf_desc.class_code(), interface_subclass: intf_desc.sub_class_code(), @@ -816,9 +814,7 @@ mod tests { 0x00, Some("Test CDC ACM"), cdc::UsbCdcAcmHandler::endpoints(), - Arc::new(Mutex::new( - Box::new(cdc::UsbCdcAcmHandler::new()) as Box - )), + Arc::new(cdc::UsbCdcAcmHandler::new()) as Arc, ) .unwrap()]) } @@ -1200,10 +1196,9 @@ mod tests { } fn new_iso_test_device() -> UsbDevice { - let handler = Arc::new(Mutex::new( - Box::new(util::tests::IsoLoopbackHandler::new()) - as Box, - )); + let handler: Arc = Arc::new( + util::tests::IsoLoopbackHandler::new(), + ); UsbDevice::new(0) .unwrap() .with_interface( diff --git a/lib/src/util.rs b/lib/src/util.rs index a95529d..c495f3c 100644 --- a/lib/src/util.rs +++ b/lib/src/util.rs @@ -99,12 +99,12 @@ pub(crate) mod tests { /// ISO OUT: accepts and discards data, reports success per packet. #[derive(Debug)] pub(crate) struct IsoLoopbackHandler { - counter: u8, + counter: std::sync::atomic::AtomicU8, } impl IsoLoopbackHandler { pub fn new() -> Self { - Self { counter: 0 } + Self { counter: std::sync::atomic::AtomicU8::new(0) } } } @@ -114,7 +114,7 @@ pub(crate) mod tests { } fn handle_urb( - &mut self, + &self, _interface: &UsbInterface, request: UrbRequest, ) -> std::io::Result { @@ -127,8 +127,7 @@ pub(crate) mod tests { let mut descriptors = Vec::new(); for desc in &request.iso_packet_descriptors { - let fill_byte = self.counter; - self.counter = self.counter.wrapping_add(1); + let fill_byte = self.counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed); let actual_len = desc.length; let packet_data = vec![fill_byte; actual_len as usize]; data.extend_from_slice(&packet_data); @@ -178,7 +177,7 @@ pub(crate) mod tests { Ok(UrbResponse::default()) } - fn as_any(&mut self) -> &mut dyn Any { + fn as_any(&self) -> &dyn Any { self } }