diff --git a/linux-rust/src/bluetooth/aacp.rs b/linux-rust/src/bluetooth/aacp.rs index bed6ca853..b3a19abb4 100644 --- a/linux-rust/src/bluetooth/aacp.rs +++ b/linux-rust/src/bluetooth/aacp.rs @@ -439,8 +439,27 @@ impl AACPManager { } async fn send_packet(&self, data: &[u8]) -> Result<()> { - let state = self.state.lock().await; - if let Some(sender) = &state.sender { + // The sender is cloned and the lock released before awaiting. Holding + // the state mutex across a full channel blocks every other task, + // including the receive loop, and the manager stops responding + // entirely. The timeout turns a stuck mutex into a logged error + // instead of a hang with no diagnosis. + let sender = match tokio::time::timeout( + std::time::Duration::from_secs(2), + self.state.lock(), + ) + .await + { + Ok(state) => state.sender.clone(), + Err(_) => { + error!("send_packet: state mutex held for over 2s, giving up"); + return Err(Error::from(std::io::Error::new( + std::io::ErrorKind::TimedOut, + "state mutex busy", + ))); + } + }; + if let Some(sender) = sender { sender.send(data.to_vec()).await.map_err(|e| { error!("Failed to send packet to channel: {}", e); Error::from(std::io::Error::new(